From 5d18907795fc1f4a9d01824ba73d26ea0b1f8ee4 Mon Sep 17 00:00:00 2001 From: Kevin Krouse Date: Thu, 3 Apr 2008 17:10:22 +0000 Subject: [PATCH 001/494] 5663 : move checkbox send to new ajax action api SVN r8466 |2008-04-03 17:10:22 +0000 --- src/org/labkey/test/bvt/SpecimenTest.java | 290 ++++++++++++++++++++++ 1 file changed, 290 insertions(+) create mode 100644 src/org/labkey/test/bvt/SpecimenTest.java diff --git a/src/org/labkey/test/bvt/SpecimenTest.java b/src/org/labkey/test/bvt/SpecimenTest.java new file mode 100644 index 00000000..722e772d --- /dev/null +++ b/src/org/labkey/test/bvt/SpecimenTest.java @@ -0,0 +1,290 @@ +package org.labkey.test.bvt; + +import org.apache.commons.lang.time.FastDateFormat; +import org.labkey.test.BaseSeleniumWebTest; +import org.labkey.test.Locator; + +import java.io.*; +import java.util.Date; + +/** + * User: brittp + * Date: Mar 9, 2006 + * Time: 1:54:57 PM + */ +public class SpecimenTest extends BaseSeleniumWebTest +{ + protected static final String PROJECT_NAME = "SpecimenVerifyProject"; + protected static final String FOLDER_NAME = "My Study"; + private static final String SPECIMEN_ARCHIVE = "/sampledata/study/sample_a.specimens"; + private static final String ARCHIVE_TEMP_DIR = "/sampledata/study/drt_temp"; + private static final int MAX_WAIT_SECONDS = 4*60; + private String _studyDataRoot = null; + private int _completedSpecimenImports = 0; + + + public String getAssociatedModuleDirectory() + { + return "study"; + } + + @Override + protected boolean isFileUploadTest() + { + return true; + } + + protected void doCleanup() throws Exception + { + _studyDataRoot = getLabKeyRoot() + "/sampledata/study"; + try + { + File tempDir = new File(getLabKeyRoot() + ARCHIVE_TEMP_DIR); + for (File file : tempDir.listFiles()) + file.delete(); + tempDir.delete(); + } + catch (Exception e) + { + // swallow any failures. + } + try { deleteFolder(PROJECT_NAME, FOLDER_NAME); } catch (Throwable e) {} + try { deleteProject(PROJECT_NAME); } catch (Throwable e) {} + } + + + protected void doTestSteps() + { + _studyDataRoot = getLabKeyRoot() + "/sampledata/study"; + + createProject(PROJECT_NAME); + createSubfolder(PROJECT_NAME, PROJECT_NAME, FOLDER_NAME, "Study", null); + clickNavButton("Create Study"); + click(Locator.checkboxByNameAndValue("simpleRepository", "false", true)); + clickNavButton("Create Study"); + clickLinkWithText("My Study"); + importSpecimenArchive(SPECIMEN_ARCHIVE); + + // specimen management setup + selenium.click("link=My Study Study"); + selenium.waitForPageToLoad("30000"); + selenium.click("link=Manage Statuses"); + selenium.waitForPageToLoad("30000"); + selenium.type("newLabel", "New Request"); + clickNavButton("Save"); + selenium.waitForPageToLoad("30000"); + selenium.type("newLabel", "Processing"); + clickNavButton("Save"); + selenium.waitForPageToLoad("30000"); + selenium.type("newLabel", "Completed"); + selenium.click("newFinalState"); + clickNavButton("Save"); + selenium.waitForPageToLoad("30000"); + selenium.type("newLabel", "Rejected"); + selenium.click("newFinalState"); + selenium.click("newSpecimensLocked"); + clickNavButton("Done"); + selenium.waitForPageToLoad("30000"); + selenium.click("link=Manage Actors and Groups"); + selenium.waitForPageToLoad("30000"); + selenium.type("newLabel", "SLG"); + selenium.select("newPerSite", "label=One Per Study"); + clickNavButton("Save"); + selenium.waitForPageToLoad("30000"); + selenium.type("newLabel", "IRB"); + selenium.select("newPerSite", "label=Multiple Per Study (Location Affiliated)"); + clickNavButton("Save"); + selenium.waitForPageToLoad("30000"); + selenium.click("link=Update Members"); + selenium.waitForPageToLoad("30000"); + clickNavButton("Update Members"); + selenium.waitForPageToLoad("30000"); + selenium.click("//a[contains(@href, 'manageActors.view?showMemberSites')]"); + selenium.waitForPageToLoad("30000"); + selenium.click("link=My Study Study"); + selenium.waitForPageToLoad("30000"); + selenium.click("link=Manage Default Requirements"); + selenium.waitForPageToLoad("30000"); + selenium.select("originatorActor", "label=IRB"); + selenium.type("originatorDescription", "Originating IRB Approval"); + clickNavButton("Add Requirement"); + selenium.waitForPageToLoad("30000"); + selenium.select("providerActor", "label=IRB"); + selenium.type("providerDescription", "Providing IRB Approval"); + selenium.click("//tr[2]/td[3]/input"); + selenium.waitForPageToLoad("30000"); + selenium.select("receiverActor", "label=IRB"); + selenium.type("receiverDescription", "Receiving IRB Approval"); + selenium.click("//tr[2]/td[3]/input"); + selenium.waitForPageToLoad("30000"); + selenium.select("generalActor", "label=SLG"); + selenium.type("generalDescription", "SLG Approval"); + selenium.click("//tr[2]/td[3]/input"); + selenium.waitForPageToLoad("30000"); + selenium.click("link=Manage Study"); + selenium.waitForPageToLoad("30000"); + selenium.click("link=Manage New Request Form"); + selenium.waitForPageToLoad("30000"); + clickNavButton("Add New Input", 0); + selenium.type("document.forms[0].title[3]", "Last One"); + selenium.type("document.forms[0].helpText[3]", "A test input"); + selenium.click("document.forms[0].required[3]"); + clickNavButton("Save"); + selenium.waitForPageToLoad("30000"); + selenium.click("link=My Study Study"); + selenium.waitForPageToLoad("30000"); + + // create request + selenium.click("link=Plasma, Unknown Processing"); + selenium.waitForPageToLoad("30000"); + selenium.click(".toggle"); + clickNavButton("Request Options", 0); + clickLinkWithText("Create New Request"); + selenium.waitForPageToLoad("30000"); + selenium.select("destinationSite", "label=Aurum Health KOSH Lab, Orkney, South Africa (Repository)"); + selenium.type("input0", "Assay Plan"); + selenium.type("input2", "Comments"); + selenium.type("input1", "Shipping"); + clickNavButton("Create Request"); + selenium.waitForPageToLoad("30000"); + assertTextPresent("Please provide all required input."); + selenium.type("input3", "sample last one input"); + clickNavButton("Create Request"); + selenium.waitForPageToLoad("30000"); + assertTextPresent("sample last one input"); + assertTextPresent("IRB"); + assertTextPresent("KCMC, Moshi, Tanzania"); + assertTextPresent("Originating IRB Approval"); + assertTextPresent("Contract Lab Services, Johannesburg, South Africa (Repository)"); + assertTextPresent("Providing IRB Approval"); + assertTextPresent("Aurum Health KOSH Lab, Orkney, South Africa (Repository)"); + assertTextPresent("Receiving IRB Approval"); + assertTextPresent("SLG"); + assertTextPresent("SLG Approval"); + assertTextPresent("BAA07XNP-01"); + // verify that the swab specimen isn't present yet + assertTextNotPresent("DAA07YGW-01"); + assertTextNotPresent("Complete"); + selenium.click("link=My Study Study"); + selenium.waitForPageToLoad("30000"); + + // add additional specimens + selenium.click("link=Swab"); + selenium.waitForPageToLoad("30000"); + selenium.click(".toggle"); + clickNavButton("Request Options", 0); + clickLinkWithText("Add To Existing Request"); + selenium.waitForPageToLoad("30000"); + clickNavButton("Select"); + selenium.waitForPageToLoad("30000"); + assertTextPresent("sample last one input"); + assertTextPresent("IRB"); + assertTextPresent("KCMC, Moshi, Tanzania"); + assertTextPresent("Originating IRB Approval"); + assertTextPresent("Contract Lab Services, Johannesburg, South Africa (Repository)"); + assertTextPresent("Providing IRB Approval"); + assertTextPresent("Aurum Health KOSH Lab, Orkney, South Africa (Repository)"); + assertTextPresent("Receiving IRB Approval"); + assertTextPresent("SLG"); + assertTextPresent("SLG Approval"); + assertTextPresent("BAA07XNP-01"); + assertTextPresent("DAA07YGW-01"); + + // submit request + assertTextPresent("Not Yet Submitted"); + assertTextNotPresent("New Request"); + clickNavButton("Submit Request"); + assertTrue(selenium.getConfirmation().matches("^Once a request is submitted, its specimen list may no longer be modified\\. Continue[\\s\\S]$")); + assertTextNotPresent("Not Yet Submitted"); + assertTextPresent("New Request"); + + // modify request + selenium.select("newActor", "label=SLG"); + selenium.type("newDescription", "Other SLG Approval"); + clickNavButton("Add Requirement"); + selenium.waitForPageToLoad("30000"); + selenium.click("//a[contains(@href, 'manageRequirement.view')]"); + selenium.waitForPageToLoad("30000"); + selenium.click("complete"); + clickNavButton("Save Changes and Send Notifications"); + selenium.waitForPageToLoad("30000"); + assertTextPresent("Complete"); + + // verify views + selenium.click("link=View History"); + selenium.waitForPageToLoad("30000"); + assertTextPresent("Request submitted for processing."); + selenium.click("link=View Request"); + selenium.waitForPageToLoad("30000"); + selenium.click("link=Originating Location Specimen Lists"); + selenium.waitForPageToLoad("30000"); + assertTextPresent("KCMC, Moshi, Tanzania"); + clickNavButton("Cancel"); + selenium.click("link=Providing Location Specimen Lists"); + selenium.waitForPageToLoad("30000"); + assertTextPresent("Contract Lab Services, Johannesburg, South Africa (Repository)"); + clickNavButton("Cancel"); + + // cancel request + selenium.click("link=Update Status"); + selenium.waitForPageToLoad("30000"); + selenium.select("status", "label=Not Yet Submitted"); + clickNavButton("Save Changes and Send Notifications"); + clickNavButton("Cancel Request"); + assertTrue(selenium.getConfirmation().matches("^Canceling will permanently delete this pending request\\. Continue[\\s\\S]$")); + assertTextPresent("No data to show."); + clickLinkWithText("My Study Study"); + clickLinkWithText("Swab"); + selenium.click(".toggle"); + clickNavButton("Request Options", 0); + clickLinkWithText("Create New Request"); + clickNavButton("Cancel"); + } + + + protected void importSpecimenArchive(String archivePath) + { + clickLinkWithText("Data Pipeline"); + clickNavButton("Setup"); + setFormElement("path", _studyDataRoot); + submit(); + clickNavButton("View Status"); + + log("Starting import of specimen archive " + archivePath); + File copiedArchive = new File(new File(getLabKeyRoot() + ARCHIVE_TEMP_DIR), FastDateFormat.getInstance("MMddHHmmss").format(new Date()) + ".specimens"); + File specimenArchive = new File(getLabKeyRoot() + archivePath); + // copy the file into its own directory + copyFile(specimenArchive, copiedArchive); + + clickNavButton("Process and Import Data"); + String tempDirShortName = ARCHIVE_TEMP_DIR.substring(ARCHIVE_TEMP_DIR.lastIndexOf('/') + 1); + clickLinkWithText(tempDirShortName); + clickNavButton("Import specimen data"); + clickNavButton("Start Import"); + + // Unfortunately isLinkWithTextPresent also picks up the "Errors" link in the header. + startTimer(); + while (countLinksWithText("COMPLETE") == 0 && !isLinkPresentWithText("ERROR") && elapsedSeconds() < MAX_WAIT_SECONDS) + { + log("Waiting for specimen import..."); + sleep(1000); + refresh(); + } + // Unfortunately assertNotLinkWithText also picks up the "Errors" link in the header. + assertLinkNotPresentWithText("ERROR"); // Must be surrounded by an anchor tag. + assertLinkPresentWithTextCount("COMPLETE", 1); + _completedSpecimenImports++; + copiedArchive.delete(); + } + + long start = 0; + private void startTimer() + { + start = System.currentTimeMillis(); + } + + private int elapsedSeconds() + { + return (int)((System.currentTimeMillis() - start) / 1000); + } +} From d18b55cd78926b643b366836c813bb4e01d8e577 Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Tue, 13 May 2008 21:57:03 +0000 Subject: [PATCH 002/494] Add/fix copyright notices on all Java files. SVN r8743 |2008-05-13 21:57:03 +0000 --- src/org/labkey/test/bvt/SpecimenTest.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/org/labkey/test/bvt/SpecimenTest.java b/src/org/labkey/test/bvt/SpecimenTest.java index 722e772d..20a68c2a 100644 --- a/src/org/labkey/test/bvt/SpecimenTest.java +++ b/src/org/labkey/test/bvt/SpecimenTest.java @@ -1,3 +1,19 @@ +/* + * Copyright (c) 2007-2008 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.test.bvt; import org.apache.commons.lang.time.FastDateFormat; From 901850bf10b590a6536437e4fb929e10e8b7f239 Mon Sep 17 00:00:00 2001 From: Erik Ulberg Date: Tue, 1 Jul 2008 20:28:05 +0000 Subject: [PATCH 003/494] Fix for 6133: check for null value of getName, and present nice error instead of NPE trace ExperimentController.java Fix for 6089: Small fixes to make Selenium work in IE, also Selenium 1.0 beta added, TestHelper edited to allow testing in IE Fix for 5370: Added MicroArrayTest.java Fix for 5729: Added Run Group testing to MS2BvtTest.java Fix for 6135: Added testing of dataset row editing to StudyBvtTest.java Fix for 6097: Added button to load applicable user props to TestHelper, also TestHelper now keeps whatever you ran last as its default setting for the next run Fix for 6126: Added testing of specific study permissions test SVN r9075 |2008-07-01 20:28:05 +0000 --- src/org/labkey/test/bvt/SpecimenTest.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/org/labkey/test/bvt/SpecimenTest.java b/src/org/labkey/test/bvt/SpecimenTest.java index 20a68c2a..104e4f9e 100644 --- a/src/org/labkey/test/bvt/SpecimenTest.java +++ b/src/org/labkey/test/bvt/SpecimenTest.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - + package org.labkey.test.bvt; import org.apache.commons.lang.time.FastDateFormat; @@ -64,7 +64,6 @@ protected void doCleanup() throws Exception { // swallow any failures. } - try { deleteFolder(PROJECT_NAME, FOLDER_NAME); } catch (Throwable e) {} try { deleteProject(PROJECT_NAME); } catch (Throwable e) {} } @@ -127,15 +126,15 @@ protected void doTestSteps() selenium.waitForPageToLoad("30000"); selenium.select("providerActor", "label=IRB"); selenium.type("providerDescription", "Providing IRB Approval"); - selenium.click("//tr[2]/td[3]/input"); + selenium.click("//input[@name='providerDescription']/../../td/input[@value='Add Requirement']"); selenium.waitForPageToLoad("30000"); selenium.select("receiverActor", "label=IRB"); selenium.type("receiverDescription", "Receiving IRB Approval"); - selenium.click("//tr[2]/td[3]/input"); + selenium.click("//input[@name='receiverDescription']/../../td/input[@value='Add Requirement']"); selenium.waitForPageToLoad("30000"); selenium.select("generalActor", "label=SLG"); selenium.type("generalDescription", "SLG Approval"); - selenium.click("//tr[2]/td[3]/input"); + selenium.click("//input[@name='generalDescription']/../../td/input[@value='Add Requirement']"); selenium.waitForPageToLoad("30000"); selenium.click("link=Manage Study"); selenium.waitForPageToLoad("30000"); From edb7d92fc59521527e6c09d7a89ac90d8dcdb425 Mon Sep 17 00:00:00 2001 From: Bma Clean Date: Sat, 16 Aug 2008 13:06:59 +0000 Subject: [PATCH 004/494] New pipeline BVT (phase 1) - ms2 successful samples and fractions - full search retry - pipeline email SVN r9333 |2008-08-16 13:06:59 +0000 --- src/org/labkey/test/bvt/SpecimenTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/org/labkey/test/bvt/SpecimenTest.java b/src/org/labkey/test/bvt/SpecimenTest.java index 104e4f9e..fc7ff7de 100644 --- a/src/org/labkey/test/bvt/SpecimenTest.java +++ b/src/org/labkey/test/bvt/SpecimenTest.java @@ -20,7 +20,7 @@ import org.labkey.test.BaseSeleniumWebTest; import org.labkey.test.Locator; -import java.io.*; +import java.io.File; import java.util.Date; /** @@ -263,7 +263,7 @@ protected void importSpecimenArchive(String archivePath) clickNavButton("Setup"); setFormElement("path", _studyDataRoot); submit(); - clickNavButton("View Status"); + clickLinkWithText("Pipeline"); log("Starting import of specimen archive " + archivePath); File copiedArchive = new File(new File(getLabKeyRoot() + ARCHIVE_TEMP_DIR), FastDateFormat.getInstance("MMddHHmmss").format(new Date()) + ".specimens"); From 14b52e5af067a0a9e285c4c266a2a245a2d9810f Mon Sep 17 00:00:00 2001 From: Erik Ulberg Date: Fri, 22 Aug 2008 19:19:00 +0000 Subject: [PATCH 005/494] Converted the buttons to use css and deleted ButtonServlet. The buttons are now more interactive and are either a span wrapped in a link or an input wrapped in a span. Fix for the header lines with an extra pixel. SVN r9365 |2008-08-22 19:19:00 +0000 --- src/org/labkey/test/bvt/SpecimenTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/org/labkey/test/bvt/SpecimenTest.java b/src/org/labkey/test/bvt/SpecimenTest.java index fc7ff7de..f6a63f4d 100644 --- a/src/org/labkey/test/bvt/SpecimenTest.java +++ b/src/org/labkey/test/bvt/SpecimenTest.java @@ -126,15 +126,15 @@ protected void doTestSteps() selenium.waitForPageToLoad("30000"); selenium.select("providerActor", "label=IRB"); selenium.type("providerDescription", "Providing IRB Approval"); - selenium.click("//input[@name='providerDescription']/../../td/input[@value='Add Requirement']"); + selenium.click("//input[@name='providerDescription']/../.." + Locator.navSubmitButton("Add Requirement").getPath()); selenium.waitForPageToLoad("30000"); selenium.select("receiverActor", "label=IRB"); selenium.type("receiverDescription", "Receiving IRB Approval"); - selenium.click("//input[@name='receiverDescription']/../../td/input[@value='Add Requirement']"); + selenium.click("//input[@name='receiverDescription']/../.." + Locator.navSubmitButton("Add Requirement").getPath()); selenium.waitForPageToLoad("30000"); selenium.select("generalActor", "label=SLG"); selenium.type("generalDescription", "SLG Approval"); - selenium.click("//input[@name='generalDescription']/../../td/input[@value='Add Requirement']"); + selenium.click("//input[@name='generalDescription']/../.." + Locator.navSubmitButton("Add Requirement").getPath()); selenium.waitForPageToLoad("30000"); selenium.click("link=Manage Study"); selenium.waitForPageToLoad("30000"); From 7255e8693116195712d15ba4ce601e497bc8dd16 Mon Sep 17 00:00:00 2001 From: Jess Garms Date: Fri, 10 Oct 2008 21:04:20 +0000 Subject: [PATCH 006/494] BVT fixes for button changes. SVN r9685 |2008-10-10 21:04:20 +0000 --- src/org/labkey/test/bvt/SpecimenTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/org/labkey/test/bvt/SpecimenTest.java b/src/org/labkey/test/bvt/SpecimenTest.java index f6a63f4d..4524d483 100644 --- a/src/org/labkey/test/bvt/SpecimenTest.java +++ b/src/org/labkey/test/bvt/SpecimenTest.java @@ -126,15 +126,15 @@ protected void doTestSteps() selenium.waitForPageToLoad("30000"); selenium.select("providerActor", "label=IRB"); selenium.type("providerDescription", "Providing IRB Approval"); - selenium.click("//input[@name='providerDescription']/../.." + Locator.navSubmitButton("Add Requirement").getPath()); + selenium.click("//input[@name='providerDescription']/../.." + Locator.navButton("Add Requirement").getPath()); selenium.waitForPageToLoad("30000"); selenium.select("receiverActor", "label=IRB"); selenium.type("receiverDescription", "Receiving IRB Approval"); - selenium.click("//input[@name='receiverDescription']/../.." + Locator.navSubmitButton("Add Requirement").getPath()); + selenium.click("//input[@name='receiverDescription']/../.." + Locator.navButton("Add Requirement").getPath()); selenium.waitForPageToLoad("30000"); selenium.select("generalActor", "label=SLG"); selenium.type("generalDescription", "SLG Approval"); - selenium.click("//input[@name='generalDescription']/../.." + Locator.navSubmitButton("Add Requirement").getPath()); + selenium.click("//input[@name='generalDescription']/../.." + Locator.navButton("Add Requirement").getPath()); selenium.waitForPageToLoad("30000"); selenium.click("link=Manage Study"); selenium.waitForPageToLoad("30000"); From a1908a78506a9da77535dd2c119548f3952bd4b3 Mon Sep 17 00:00:00 2001 From: Jess Garms Date: Mon, 3 Nov 2008 18:47:33 +0000 Subject: [PATCH 007/494] 6622 : Get rid of use of tabloader in StudyServiceImpl.insertDatasetRow() Also fix up the TestHelper to sort tests by name, and stop persisting the root incorrectly across branches. SVN r9781 |2008-11-03 18:47:33 +0000 --- src/org/labkey/test/bvt/SpecimenTest.java | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/src/org/labkey/test/bvt/SpecimenTest.java b/src/org/labkey/test/bvt/SpecimenTest.java index 4524d483..036fb7d8 100644 --- a/src/org/labkey/test/bvt/SpecimenTest.java +++ b/src/org/labkey/test/bvt/SpecimenTest.java @@ -53,17 +53,11 @@ protected boolean isFileUploadTest() protected void doCleanup() throws Exception { _studyDataRoot = getLabKeyRoot() + "/sampledata/study"; - try - { - File tempDir = new File(getLabKeyRoot() + ARCHIVE_TEMP_DIR); - for (File file : tempDir.listFiles()) - file.delete(); - tempDir.delete(); - } - catch (Exception e) - { - // swallow any failures. - } + File tempDir = new File(getLabKeyRoot() + ARCHIVE_TEMP_DIR); + for (File file : tempDir.listFiles()) + file.delete(); + tempDir.delete(); + try { deleteProject(PROJECT_NAME); } catch (Throwable e) {} } From a220cb25e2e46c3a0fc002d1ebcc204910de3bf6 Mon Sep 17 00:00:00 2001 From: Britt Piehler Date: Sat, 10 Jan 2009 21:52:50 +0000 Subject: [PATCH 008/494] API/Ext-based management of specimen requests. Implemented as a two-phase project: first, I created server-side specimen APIs and a Specimen.js client API class to enable all required specimen and specimen request functionality, and then I modified the specimen views to include an Ext-based UI that utilizes these APIs. This approach is more flexible long-term, but required significant changes to a number of areas. I've run DRTs and BVTs, and bootstrapped both systems. SVN r10048 |2009-01-10 21:52:50 +0000 --- src/org/labkey/test/bvt/SpecimenTest.java | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/org/labkey/test/bvt/SpecimenTest.java b/src/org/labkey/test/bvt/SpecimenTest.java index 036fb7d8..cc4e12d0 100644 --- a/src/org/labkey/test/bvt/SpecimenTest.java +++ b/src/org/labkey/test/bvt/SpecimenTest.java @@ -154,11 +154,11 @@ protected void doTestSteps() selenium.type("input0", "Assay Plan"); selenium.type("input2", "Comments"); selenium.type("input1", "Shipping"); - clickNavButton("Create Request"); + clickNavButton("Create and View Details"); selenium.waitForPageToLoad("30000"); assertTextPresent("Please provide all required input."); selenium.type("input3", "sample last one input"); - clickNavButton("Create Request"); + clickNavButton("Create and View Details"); selenium.waitForPageToLoad("30000"); assertTextPresent("sample last one input"); assertTextPresent("IRB"); @@ -182,9 +182,15 @@ protected void doTestSteps() selenium.waitForPageToLoad("30000"); selenium.click(".toggle"); clickNavButton("Request Options", 0); - clickLinkWithText("Add To Existing Request"); - selenium.waitForPageToLoad("30000"); - clickNavButton("Select"); + clickLinkWithText("Add To Existing Request", false); + sleep(5000); + clickNavButton("Add 8 Vials to Request", 0); + sleep(5000); + clickNavButton("OK", 0); + sleep(5000); + clickNavButton("Request Options", 0); + clickLinkWithText("View Existing Requests"); + clickNavButton("Details"); selenium.waitForPageToLoad("30000"); assertTextPresent("sample last one input"); assertTextPresent("IRB"); From 0c47443fd0c18d145aac5b4061e4c370b7e90530 Mon Sep 17 00:00:00 2001 From: Britt Piehler Date: Sun, 11 Jan 2009 01:17:12 +0000 Subject: [PATCH 009/494] Trying a different method of waiting for the new Exp specimen UI to show and hide in the specimen test; we hit a race condition in the last BVT run. SVN r10050 |2009-01-11 01:17:12 +0000 --- src/org/labkey/test/bvt/SpecimenTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/org/labkey/test/bvt/SpecimenTest.java b/src/org/labkey/test/bvt/SpecimenTest.java index cc4e12d0..e7184fe7 100644 --- a/src/org/labkey/test/bvt/SpecimenTest.java +++ b/src/org/labkey/test/bvt/SpecimenTest.java @@ -183,11 +183,11 @@ protected void doTestSteps() selenium.click(".toggle"); clickNavButton("Request Options", 0); clickLinkWithText("Add To Existing Request", false); - sleep(5000); + waitForText("Add 8 Vials to Request", 30000); clickNavButton("Add 8 Vials to Request", 0); - sleep(5000); + waitForText("successfully added to request", 30000); clickNavButton("OK", 0); - sleep(5000); + waitForText("Request Options", 30000); clickNavButton("Request Options", 0); clickLinkWithText("View Existing Requests"); clickNavButton("Details"); From c210e171adf2aa778bd41266fcd29429a497d259 Mon Sep 17 00:00:00 2001 From: Britt Piehler Date: Sun, 11 Jan 2009 03:34:23 +0000 Subject: [PATCH 010/494] Another attempt at fixing a specimentest race condition. SVN r10051 |2009-01-11 03:34:23 +0000 --- src/org/labkey/test/bvt/SpecimenTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/org/labkey/test/bvt/SpecimenTest.java b/src/org/labkey/test/bvt/SpecimenTest.java index e7184fe7..ee559ffd 100644 --- a/src/org/labkey/test/bvt/SpecimenTest.java +++ b/src/org/labkey/test/bvt/SpecimenTest.java @@ -183,11 +183,11 @@ protected void doTestSteps() selenium.click(".toggle"); clickNavButton("Request Options", 0); clickLinkWithText("Add To Existing Request", false); - waitForText("Add 8 Vials to Request", 30000); + waitForElement(Locator.extButton("Add 8 Vials to Request"), 30000); clickNavButton("Add 8 Vials to Request", 0); - waitForText("successfully added to request", 30000); + waitForElement(Locator.extButton("OK"), 30000); clickNavButton("OK", 0); - waitForText("Request Options", 30000); + sleep(10000); clickNavButton("Request Options", 0); clickLinkWithText("View Existing Requests"); clickNavButton("Details"); From e50c654e42b8fe0b71b7b2a9b326ac987a6c98c7 Mon Sep 17 00:00:00 2001 From: Britt Piehler Date: Sun, 11 Jan 2009 05:56:38 +0000 Subject: [PATCH 011/494] Third time's the charm? I'm adding hard 15 second waits between clicks for this fragile portion of the test; Ext asynchrony apparently doesn't play nicely with the 'waitFor...' utilities we have in our test harness. SVN r10052 |2009-01-11 05:56:38 +0000 --- src/org/labkey/test/bvt/SpecimenTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/org/labkey/test/bvt/SpecimenTest.java b/src/org/labkey/test/bvt/SpecimenTest.java index ee559ffd..24e4e218 100644 --- a/src/org/labkey/test/bvt/SpecimenTest.java +++ b/src/org/labkey/test/bvt/SpecimenTest.java @@ -183,11 +183,11 @@ protected void doTestSteps() selenium.click(".toggle"); clickNavButton("Request Options", 0); clickLinkWithText("Add To Existing Request", false); - waitForElement(Locator.extButton("Add 8 Vials to Request"), 30000); + sleep(15000); clickNavButton("Add 8 Vials to Request", 0); - waitForElement(Locator.extButton("OK"), 30000); + sleep(15000); clickNavButton("OK", 0); - sleep(10000); + sleep(15000); clickNavButton("Request Options", 0); clickLinkWithText("View Existing Requests"); clickNavButton("Details"); From 2e669ea2bff7c41f9a8f54cb911978fbde4f18bd Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Fri, 16 Jan 2009 18:59:00 +0000 Subject: [PATCH 012/494] Add a few licenses; update other copyright dates to 2009. SVN r10095 |2009-01-16 18:59:00 +0000 --- src/org/labkey/test/bvt/SpecimenTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/org/labkey/test/bvt/SpecimenTest.java b/src/org/labkey/test/bvt/SpecimenTest.java index 24e4e218..1b99ba49 100644 --- a/src/org/labkey/test/bvt/SpecimenTest.java +++ b/src/org/labkey/test/bvt/SpecimenTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007-2008 LabKey Corporation + * Copyright (c) 2007-2009 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. From 667e33b9142eee032e945bd6824b7d0238bb791d Mon Sep 17 00:00:00 2001 From: Josh Eckels Date: Thu, 2 Apr 2009 17:00:31 +0000 Subject: [PATCH 013/494] Add test for metadata overrides on custom queries and built-in tables Add test for microarray bulk properties upload Add separate test harness methods for clicking on radio buttons, instead of having clickCheckbox() take a boolean to indicate if it's a radio button or checkbox Fix NAb upload by give EnumTableInfos names Better user feedback on saving metdata SVN r10766 |2009-04-02 17:00:31 +0000 --- src/org/labkey/test/bvt/SpecimenTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/org/labkey/test/bvt/SpecimenTest.java b/src/org/labkey/test/bvt/SpecimenTest.java index 1b99ba49..4a29630b 100644 --- a/src/org/labkey/test/bvt/SpecimenTest.java +++ b/src/org/labkey/test/bvt/SpecimenTest.java @@ -69,7 +69,7 @@ protected void doTestSteps() createProject(PROJECT_NAME); createSubfolder(PROJECT_NAME, PROJECT_NAME, FOLDER_NAME, "Study", null); clickNavButton("Create Study"); - click(Locator.checkboxByNameAndValue("simpleRepository", "false", true)); + click(Locator.radioButtonByNameAndValue("simpleRepository", "false")); clickNavButton("Create Study"); clickLinkWithText("My Study"); importSpecimenArchive(SPECIMEN_ARCHIVE); From 1c9810fea5b22f15d4d26f7d4864fcc67398de84 Mon Sep 17 00:00:00 2001 From: Matthew Bellew Date: Tue, 12 May 2009 22:58:15 +0000 Subject: [PATCH 014/494] use FileBrowser for pipeline UI SVN r10992 |2009-05-12 22:58:15 +0000 --- src/org/labkey/test/bvt/SpecimenTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/org/labkey/test/bvt/SpecimenTest.java b/src/org/labkey/test/bvt/SpecimenTest.java index 4a29630b..be93725e 100644 --- a/src/org/labkey/test/bvt/SpecimenTest.java +++ b/src/org/labkey/test/bvt/SpecimenTest.java @@ -273,8 +273,8 @@ protected void importSpecimenArchive(String archivePath) clickNavButton("Process and Import Data"); String tempDirShortName = ARCHIVE_TEMP_DIR.substring(ARCHIVE_TEMP_DIR.lastIndexOf('/') + 1); - clickLinkWithText(tempDirShortName); - clickNavButton("Import specimen data"); + waitAndClick(Locator.fileTreeByName(tempDirShortName)); + waitAndClickNavButton("Import specimen data"); clickNavButton("Start Import"); // Unfortunately isLinkWithTextPresent also picks up the "Errors" link in the header. From 35985c936277b9abac40aa6b8be7ea371db9da41 Mon Sep 17 00:00:00 2001 From: Josh Eckels Date: Fri, 22 May 2009 22:30:44 +0000 Subject: [PATCH 015/494] 7875 : Remove space between sections in the assay designer 7914 : SampleSets: NPE in DomainForeignKey.convertProperties 7901 : Specimen ID handling in Luminex Tests for MS2 sample description assay provider, specimen lookups from assays SVN r11103 |2009-05-22 22:30:44 +0000 --- src/org/labkey/test/bvt/SpecimenTest.java | 59 +++-------------------- 1 file changed, 8 insertions(+), 51 deletions(-) diff --git a/src/org/labkey/test/bvt/SpecimenTest.java b/src/org/labkey/test/bvt/SpecimenTest.java index be93725e..82ba91b3 100644 --- a/src/org/labkey/test/bvt/SpecimenTest.java +++ b/src/org/labkey/test/bvt/SpecimenTest.java @@ -16,12 +16,10 @@ package org.labkey.test.bvt; -import org.apache.commons.lang.time.FastDateFormat; import org.labkey.test.BaseSeleniumWebTest; import org.labkey.test.Locator; import java.io.File; -import java.util.Date; /** * User: brittp @@ -36,7 +34,6 @@ public class SpecimenTest extends BaseSeleniumWebTest private static final String ARCHIVE_TEMP_DIR = "/sampledata/study/drt_temp"; private static final int MAX_WAIT_SECONDS = 4*60; private String _studyDataRoot = null; - private int _completedSpecimenImports = 0; public String getAssociatedModuleDirectory() @@ -72,7 +69,14 @@ protected void doTestSteps() click(Locator.radioButtonByNameAndValue("simpleRepository", "false")); clickNavButton("Create Study"); clickLinkWithText("My Study"); - importSpecimenArchive(SPECIMEN_ARCHIVE); + + clickLinkWithText("Data Pipeline"); + clickNavButton("Setup"); + setFormElement("path", _studyDataRoot); + submit(); + clickLinkWithText("Pipeline"); + + importSpecimenArchive(new File(getLabKeyRoot(), SPECIMEN_ARCHIVE), new File(getLabKeyRoot(), ARCHIVE_TEMP_DIR), FOLDER_NAME, 0); // specimen management setup selenium.click("link=My Study Study"); @@ -255,51 +259,4 @@ protected void doTestSteps() clickLinkWithText("Create New Request"); clickNavButton("Cancel"); } - - - protected void importSpecimenArchive(String archivePath) - { - clickLinkWithText("Data Pipeline"); - clickNavButton("Setup"); - setFormElement("path", _studyDataRoot); - submit(); - clickLinkWithText("Pipeline"); - - log("Starting import of specimen archive " + archivePath); - File copiedArchive = new File(new File(getLabKeyRoot() + ARCHIVE_TEMP_DIR), FastDateFormat.getInstance("MMddHHmmss").format(new Date()) + ".specimens"); - File specimenArchive = new File(getLabKeyRoot() + archivePath); - // copy the file into its own directory - copyFile(specimenArchive, copiedArchive); - - clickNavButton("Process and Import Data"); - String tempDirShortName = ARCHIVE_TEMP_DIR.substring(ARCHIVE_TEMP_DIR.lastIndexOf('/') + 1); - waitAndClick(Locator.fileTreeByName(tempDirShortName)); - waitAndClickNavButton("Import specimen data"); - clickNavButton("Start Import"); - - // Unfortunately isLinkWithTextPresent also picks up the "Errors" link in the header. - startTimer(); - while (countLinksWithText("COMPLETE") == 0 && !isLinkPresentWithText("ERROR") && elapsedSeconds() < MAX_WAIT_SECONDS) - { - log("Waiting for specimen import..."); - sleep(1000); - refresh(); - } - // Unfortunately assertNotLinkWithText also picks up the "Errors" link in the header. - assertLinkNotPresentWithText("ERROR"); // Must be surrounded by an anchor tag. - assertLinkPresentWithTextCount("COMPLETE", 1); - _completedSpecimenImports++; - copiedArchive.delete(); - } - - long start = 0; - private void startTimer() - { - start = System.currentTimeMillis(); - } - - private int elapsedSeconds() - { - return (int)((System.currentTimeMillis() - start) / 1000); - } } From a6e459080240530b65c1c4ec9e1ba7c690c1f4dc Mon Sep 17 00:00:00 2001 From: Britt Piehler Date: Wed, 24 Jun 2009 17:46:23 +0000 Subject: [PATCH 016/494] Bug fixes: 8179: lastFilter remembers assay definition's rowID 7951: 'delete and reimport' of NAb runs does not correctly re-select ptid/visit resolver or target study 7962: AssertionError in StudyManager$DatasetImportHelper.beforeImportObject() 8131: IllegalArgumentException in CommentDisplayColumn.addQueryColumns 7969: Specimen ID filled in with (random?) specimen using Participant/Visit resolver 7950: Specimen ID not recorded in NAb if ptid/visit don't match? 7958: NullPointerException in NabDataHandler.getDilutionSummaries() 7796: Update version info in executables.txt 7639: IllegalStateException in org.labkey.study.model.StudyManager.createVisit() 7961: NullPointerException in org.labkey.study.model.AllParticipantData.get() 7965: IllegalStateException in org.labkey.study.SampleManager.getSpecimen() 7870: specimen.tsv in sample_a.specimens has duplicate records with same record_id SVN r11374 |2009-06-24 17:46:23 +0000 --- src/org/labkey/test/bvt/SpecimenTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/org/labkey/test/bvt/SpecimenTest.java b/src/org/labkey/test/bvt/SpecimenTest.java index 82ba91b3..b8599f1a 100644 --- a/src/org/labkey/test/bvt/SpecimenTest.java +++ b/src/org/labkey/test/bvt/SpecimenTest.java @@ -238,12 +238,12 @@ protected void doTestSteps() selenium.click("link=Originating Location Specimen Lists"); selenium.waitForPageToLoad("30000"); assertTextPresent("KCMC, Moshi, Tanzania"); + fail(); clickNavButton("Cancel"); selenium.click("link=Providing Location Specimen Lists"); selenium.waitForPageToLoad("30000"); assertTextPresent("Contract Lab Services, Johannesburg, South Africa (Repository)"); clickNavButton("Cancel"); - // cancel request selenium.click("link=Update Status"); selenium.waitForPageToLoad("30000"); From b557f249b689a9bb6bb4b18bf1e45eaf72827d54 Mon Sep 17 00:00:00 2001 From: Britt Piehler Date: Wed, 24 Jun 2009 19:47:43 +0000 Subject: [PATCH 017/494] Remove explicit 'fail' call from SpecimenTest; this was added to stop the test at a specific point for debugging specimen imports, I just failed to remove it prior to checkin. SVN r11377 |2009-06-24 19:47:43 +0000 --- src/org/labkey/test/bvt/SpecimenTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/org/labkey/test/bvt/SpecimenTest.java b/src/org/labkey/test/bvt/SpecimenTest.java index b8599f1a..4774d38b 100644 --- a/src/org/labkey/test/bvt/SpecimenTest.java +++ b/src/org/labkey/test/bvt/SpecimenTest.java @@ -238,7 +238,6 @@ protected void doTestSteps() selenium.click("link=Originating Location Specimen Lists"); selenium.waitForPageToLoad("30000"); assertTextPresent("KCMC, Moshi, Tanzania"); - fail(); clickNavButton("Cancel"); selenium.click("link=Providing Location Specimen Lists"); selenium.waitForPageToLoad("30000"); From 3a21c8e48e5655bb61a66251620e24769508fb51 Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Thu, 13 Aug 2009 18:00:38 +0000 Subject: [PATCH 018/494] Clean up study / specimen test sample data: - Move unused study sample data files to obsolete directory - Eliminate makeplates step - Reorganize remaining files into directory structure matching standard export layout - Use one directory structure and set of files across all tests (both manual and import) SVN r11606 |2009-08-13 18:00:38 +0000 --- src/org/labkey/test/bvt/SpecimenTest.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/org/labkey/test/bvt/SpecimenTest.java b/src/org/labkey/test/bvt/SpecimenTest.java index 4774d38b..83a28ed5 100644 --- a/src/org/labkey/test/bvt/SpecimenTest.java +++ b/src/org/labkey/test/bvt/SpecimenTest.java @@ -30,9 +30,8 @@ public class SpecimenTest extends BaseSeleniumWebTest { protected static final String PROJECT_NAME = "SpecimenVerifyProject"; protected static final String FOLDER_NAME = "My Study"; - private static final String SPECIMEN_ARCHIVE = "/sampledata/study/sample_a.specimens"; - private static final String ARCHIVE_TEMP_DIR = "/sampledata/study/drt_temp"; - private static final int MAX_WAIT_SECONDS = 4*60; + private static final String SPECIMEN_ARCHIVE = "/sampledata/study/specimens/sample_a.specimens"; + private static final String SPECIMEN_TEMP_DIR = "/sampledata/study/drt_temp"; private String _studyDataRoot = null; @@ -50,7 +49,7 @@ protected boolean isFileUploadTest() protected void doCleanup() throws Exception { _studyDataRoot = getLabKeyRoot() + "/sampledata/study"; - File tempDir = new File(getLabKeyRoot() + ARCHIVE_TEMP_DIR); + File tempDir = new File(getLabKeyRoot() + SPECIMEN_TEMP_DIR); for (File file : tempDir.listFiles()) file.delete(); tempDir.delete(); @@ -76,7 +75,7 @@ protected void doTestSteps() submit(); clickLinkWithText("Pipeline"); - importSpecimenArchive(new File(getLabKeyRoot(), SPECIMEN_ARCHIVE), new File(getLabKeyRoot(), ARCHIVE_TEMP_DIR), FOLDER_NAME, 0); + importSpecimenArchive(new File(_studyDataRoot), new File(getLabKeyRoot(), SPECIMEN_ARCHIVE), new File(getLabKeyRoot(), SPECIMEN_TEMP_DIR), FOLDER_NAME, 0); // specimen management setup selenium.click("link=My Study Study"); From fe68716cf29db98495c6bb1caf29931c42a89831 Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Thu, 13 Aug 2009 22:39:18 +0000 Subject: [PATCH 019/494] More study test improvements: - Add SpecimenImporter class that provides more flexibility in loading specimen archives - Start running basic study tests in parallel with specimen load in StudyImportTest Also, allow import of specimen repository settings without requiring an archive. SVN r11609 |2009-08-13 22:39:18 +0000 --- src/org/labkey/test/bvt/SpecimenTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/org/labkey/test/bvt/SpecimenTest.java b/src/org/labkey/test/bvt/SpecimenTest.java index 83a28ed5..9c9afc85 100644 --- a/src/org/labkey/test/bvt/SpecimenTest.java +++ b/src/org/labkey/test/bvt/SpecimenTest.java @@ -75,7 +75,8 @@ protected void doTestSteps() submit(); clickLinkWithText("Pipeline"); - importSpecimenArchive(new File(_studyDataRoot), new File(getLabKeyRoot(), SPECIMEN_ARCHIVE), new File(getLabKeyRoot(), SPECIMEN_TEMP_DIR), FOLDER_NAME, 0); + SpecimenImporter importer = new SpecimenImporter(new File(_studyDataRoot), new File(getLabKeyRoot(), SPECIMEN_ARCHIVE), new File(getLabKeyRoot(), SPECIMEN_TEMP_DIR), FOLDER_NAME, 0); + importer.importAndWaitForComplete(); // specimen management setup selenium.click("link=My Study Study"); From 84a6f477dc8b05134aa03bf039c05f08087b49d3 Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Thu, 10 Sep 2009 20:53:59 +0000 Subject: [PATCH 020/494] Test improvements: - Table helper functions: getTableRowCount(), getTableColumnValues() - More reliable pipeline status detection: - getPipelineStatusValues() returns all current job status values - hasError() and countComplete() methods operate on status values - Use new pipeline status detection throughout study tests SVN r11716 |2009-09-10 20:53:59 +0000 --- src/org/labkey/test/bvt/SpecimenTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/org/labkey/test/bvt/SpecimenTest.java b/src/org/labkey/test/bvt/SpecimenTest.java index 9c9afc85..939be8fb 100644 --- a/src/org/labkey/test/bvt/SpecimenTest.java +++ b/src/org/labkey/test/bvt/SpecimenTest.java @@ -75,7 +75,7 @@ protected void doTestSteps() submit(); clickLinkWithText("Pipeline"); - SpecimenImporter importer = new SpecimenImporter(new File(_studyDataRoot), new File(getLabKeyRoot(), SPECIMEN_ARCHIVE), new File(getLabKeyRoot(), SPECIMEN_TEMP_DIR), FOLDER_NAME, 0); + SpecimenImporter importer = new SpecimenImporter(new File(_studyDataRoot), new File(getLabKeyRoot(), SPECIMEN_ARCHIVE), new File(getLabKeyRoot(), SPECIMEN_TEMP_DIR), FOLDER_NAME, 1); importer.importAndWaitForComplete(); // specimen management setup From 14a4e00e9b1dd292b11b36f863f930e58466f580 Mon Sep 17 00:00:00 2001 From: Karl Lum Date: Thu, 5 Nov 2009 00:34:55 +0000 Subject: [PATCH 021/494] BVT fix SVN r12268 |2009-11-05 00:34:55 +0000 --- src/org/labkey/test/bvt/SpecimenTest.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/org/labkey/test/bvt/SpecimenTest.java b/src/org/labkey/test/bvt/SpecimenTest.java index 939be8fb..a87aff75 100644 --- a/src/org/labkey/test/bvt/SpecimenTest.java +++ b/src/org/labkey/test/bvt/SpecimenTest.java @@ -50,10 +50,12 @@ protected void doCleanup() throws Exception { _studyDataRoot = getLabKeyRoot() + "/sampledata/study"; File tempDir = new File(getLabKeyRoot() + SPECIMEN_TEMP_DIR); - for (File file : tempDir.listFiles()) - file.delete(); - tempDir.delete(); - + if (tempDir.exists()) + { + for (File file : tempDir.listFiles()) + file.delete(); + tempDir.delete(); + } try { deleteProject(PROJECT_NAME); } catch (Throwable e) {} } From 74b032c0377b602156324f1c8d5c5f190203d3c1 Mon Sep 17 00:00:00 2001 From: Karl Lum Date: Tue, 15 Dec 2009 23:27:22 +0000 Subject: [PATCH 022/494] Allow a default project pipeline root based on the site file root. Add pipeline root configuration to the project settings page. SVN r12549 |2009-12-15 23:27:22 +0000 --- src/org/labkey/test/bvt/SpecimenTest.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/org/labkey/test/bvt/SpecimenTest.java b/src/org/labkey/test/bvt/SpecimenTest.java index a87aff75..cb9ef39b 100644 --- a/src/org/labkey/test/bvt/SpecimenTest.java +++ b/src/org/labkey/test/bvt/SpecimenTest.java @@ -73,8 +73,7 @@ protected void doTestSteps() clickLinkWithText("Data Pipeline"); clickNavButton("Setup"); - setFormElement("path", _studyDataRoot); - submit(); + setPipelineRoot(_studyDataRoot); clickLinkWithText("Pipeline"); SpecimenImporter importer = new SpecimenImporter(new File(_studyDataRoot), new File(getLabKeyRoot(), SPECIMEN_ARCHIVE), new File(getLabKeyRoot(), SPECIMEN_TEMP_DIR), FOLDER_NAME, 1); From cbf8578b1b0878774659166e807b4ca3080753d0 Mon Sep 17 00:00:00 2001 From: Matthew Bellew Date: Mon, 1 Feb 2010 19:50:19 +0000 Subject: [PATCH 023/494] search box SVN r12904 |2010-02-01 19:50:19 +0000 --- src/org/labkey/test/bvt/SpecimenTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/org/labkey/test/bvt/SpecimenTest.java b/src/org/labkey/test/bvt/SpecimenTest.java index cb9ef39b..90f007c1 100644 --- a/src/org/labkey/test/bvt/SpecimenTest.java +++ b/src/org/labkey/test/bvt/SpecimenTest.java @@ -140,9 +140,9 @@ protected void doTestSteps() selenium.click("link=Manage New Request Form"); selenium.waitForPageToLoad("30000"); clickNavButton("Add New Input", 0); - selenium.type("document.forms[0].title[3]", "Last One"); - selenium.type("document.forms[0].helpText[3]", "A test input"); - selenium.click("document.forms[0].required[3]"); + selenium.type("//descendant::input[@name='title'][4]", "Last One"); + selenium.type("//descendant::input[@name='helpText'][4]", "A test input"); + selenium.click("//descendant::input[@name='required'][4]"); clickNavButton("Save"); selenium.waitForPageToLoad("30000"); selenium.click("link=My Study Study"); From 67a6aec81db8a63b70e62aff4b4069ea2708e0dd Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Mon, 1 Feb 2010 21:26:49 +0000 Subject: [PATCH 024/494] Update licenses. SVN r12910 |2010-02-01 21:26:49 +0000 --- src/org/labkey/test/bvt/SpecimenTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/org/labkey/test/bvt/SpecimenTest.java b/src/org/labkey/test/bvt/SpecimenTest.java index 90f007c1..b1aed245 100644 --- a/src/org/labkey/test/bvt/SpecimenTest.java +++ b/src/org/labkey/test/bvt/SpecimenTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007-2009 LabKey Corporation + * Copyright (c) 2007-2010 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. From 724680bdbfcc46b683fb2bb91a773c64c114e097 Mon Sep 17 00:00:00 2001 From: Trey Chadick Date: Wed, 31 Mar 2010 17:00:36 +0000 Subject: [PATCH 025/494] More consistent way to wait for Ext dialogs to appear. SVN r13395 |2010-03-31 17:00:36 +0000 --- src/org/labkey/test/bvt/SpecimenTest.java | 225 +++++++++++----------- 1 file changed, 116 insertions(+), 109 deletions(-) diff --git a/src/org/labkey/test/bvt/SpecimenTest.java b/src/org/labkey/test/bvt/SpecimenTest.java index b1aed245..b7d9b335 100644 --- a/src/org/labkey/test/bvt/SpecimenTest.java +++ b/src/org/labkey/test/bvt/SpecimenTest.java @@ -18,6 +18,8 @@ import org.labkey.test.BaseSeleniumWebTest; import org.labkey.test.Locator; +import org.labkey.test.util.ExtHelper; +import org.labkey.test.util.PasswordUtil; import java.io.File; @@ -33,6 +35,10 @@ public class SpecimenTest extends BaseSeleniumWebTest private static final String SPECIMEN_ARCHIVE = "/sampledata/study/specimens/sample_a.specimens"; private static final String SPECIMEN_TEMP_DIR = "/sampledata/study/drt_temp"; private String _studyDataRoot = null; + private static final String STUDY_NAME = "My Study Study"; + private static final String DESTINATION_SITE = "Aurum Health KOSH Lab, Orkney, South Africa (Repository)"; + private static final String USER1 = "user1@specimen.test"; + private static final String USER2 = "user2@specimen.test"; public String getAssociatedModuleDirectory() @@ -65,6 +71,11 @@ protected void doTestSteps() _studyDataRoot = getLabKeyRoot() + "/sampledata/study"; createProject(PROJECT_NAME); + enableModule(PROJECT_NAME, "Dumbster"); + addWebPart("Mail Record"); + uncheckCheckbox("emailRecordOn"); + checkCheckbox("emailRecordOn"); + createSubfolder(PROJECT_NAME, PROJECT_NAME, FOLDER_NAME, "Study", null); clickNavButton("Create Study"); click(Locator.radioButtonByNameAndValue("simpleRepository", "false")); @@ -80,98 +91,80 @@ protected void doTestSteps() importer.importAndWaitForComplete(); // specimen management setup - selenium.click("link=My Study Study"); - selenium.waitForPageToLoad("30000"); - selenium.click("link=Manage Statuses"); - selenium.waitForPageToLoad("30000"); - selenium.type("newLabel", "New Request"); + clickLinkWithText(STUDY_NAME); + clickLinkWithText("Manage Statuses"); + setFormElement("newLabel", "New Request"); clickNavButton("Save"); - selenium.waitForPageToLoad("30000"); - selenium.type("newLabel", "Processing"); + setFormElement("newLabel", "Processing"); clickNavButton("Save"); - selenium.waitForPageToLoad("30000"); - selenium.type("newLabel", "Completed"); - selenium.click("newFinalState"); + setFormElement("newLabel", "Completed"); + checkCheckbox("newFinalState"); clickNavButton("Save"); - selenium.waitForPageToLoad("30000"); - selenium.type("newLabel", "Rejected"); - selenium.click("newFinalState"); - selenium.click("newSpecimensLocked"); + setFormElement("newLabel", "Rejected"); + checkCheckbox("newFinalState"); + uncheckCheckbox("newSpecimensLocked"); clickNavButton("Done"); - selenium.waitForPageToLoad("30000"); - selenium.click("link=Manage Actors and Groups"); - selenium.waitForPageToLoad("30000"); - selenium.type("newLabel", "SLG"); - selenium.select("newPerSite", "label=One Per Study"); - clickNavButton("Save"); - selenium.waitForPageToLoad("30000"); - selenium.type("newLabel", "IRB"); - selenium.select("newPerSite", "label=Multiple Per Study (Location Affiliated)"); + + clickLinkWithText("Manage Actors and Groups"); + setFormElement("newLabel", "SLG"); + selectOptionByText("newPerSite", "One Per Study"); clickNavButton("Save"); - selenium.waitForPageToLoad("30000"); - selenium.click("link=Update Members"); - selenium.waitForPageToLoad("30000"); + clickLinkWithText("Update Members"); + setText("names", USER2); + uncheckCheckbox("sendEmail"); clickNavButton("Update Members"); - selenium.waitForPageToLoad("30000"); - selenium.click("//a[contains(@href, 'manageActors.view?showMemberSites')]"); - selenium.waitForPageToLoad("30000"); - selenium.click("link=My Study Study"); - selenium.waitForPageToLoad("30000"); - selenium.click("link=Manage Default Requirements"); - selenium.waitForPageToLoad("30000"); - selenium.select("originatorActor", "label=IRB"); - selenium.type("originatorDescription", "Originating IRB Approval"); + setFormElement("newLabel", "IRB"); + selectOptionByText("newPerSite", "Multiple Per Study (Location Affiliated)"); + clickNavButton("Save"); + clickLinkWithText("Update Members", 1); + clickLinkWithText(DESTINATION_SITE); + setText("names", USER1); + uncheckCheckbox("sendEmail"); + clickLinkWithText("Update Members"); + clickLinkWithText(STUDY_NAME); + + clickLinkWithText("Manage Default Requirements"); + selectOptionByText("originatorActor", "IRB"); + setFormElement("originatorDescription", "Originating IRB Approval"); clickNavButton("Add Requirement"); - selenium.waitForPageToLoad("30000"); - selenium.select("providerActor", "label=IRB"); - selenium.type("providerDescription", "Providing IRB Approval"); - selenium.click("//input[@name='providerDescription']/../.." + Locator.navButton("Add Requirement").getPath()); - selenium.waitForPageToLoad("30000"); - selenium.select("receiverActor", "label=IRB"); - selenium.type("receiverDescription", "Receiving IRB Approval"); - selenium.click("//input[@name='receiverDescription']/../.." + Locator.navButton("Add Requirement").getPath()); - selenium.waitForPageToLoad("30000"); - selenium.select("generalActor", "label=SLG"); - selenium.type("generalDescription", "SLG Approval"); - selenium.click("//input[@name='generalDescription']/../.." + Locator.navButton("Add Requirement").getPath()); - selenium.waitForPageToLoad("30000"); - selenium.click("link=Manage Study"); - selenium.waitForPageToLoad("30000"); - selenium.click("link=Manage New Request Form"); - selenium.waitForPageToLoad("30000"); + selectOptionByText("providerActor", "IRB"); + setFormElement("providerDescription", "Providing IRB Approval"); + clickLink(Locator.xpath("//input[@name='providerDescription']/../.." + Locator.navButton("Add Requirement").getPath())); + selectOptionByText("receiverActor", "IRB"); + setFormElement("receiverDescription", "Receiving IRB Approval"); + clickLink(Locator.xpath("//input[@name='receiverDescription']/../.." + Locator.navButton("Add Requirement").getPath())); + selectOptionByText("generalActor", "SLG"); + setFormElement("generalDescription", "SLG Approval"); + clickLink(Locator.xpath("//input[@name='generalDescription']/../.." + Locator.navButton("Add Requirement").getPath())); + clickLinkWithText("Manage Study"); + + clickLinkWithText("Manage New Request Form"); clickNavButton("Add New Input", 0); - selenium.type("//descendant::input[@name='title'][4]", "Last One"); - selenium.type("//descendant::input[@name='helpText'][4]", "A test input"); - selenium.click("//descendant::input[@name='required'][4]"); + setFormElement("//descendant::input[@name='title'][4]", "Last One"); + setFormElement("//descendant::input[@name='helpText'][4]", "A test input"); + click(Locator.xpath("//descendant::input[@name='required'][4]")); clickNavButton("Save"); - selenium.waitForPageToLoad("30000"); - selenium.click("link=My Study Study"); - selenium.waitForPageToLoad("30000"); + clickLinkWithText(STUDY_NAME); // create request - selenium.click("link=Plasma, Unknown Processing"); - selenium.waitForPageToLoad("30000"); - selenium.click(".toggle"); - clickNavButton("Request Options", 0); - clickLinkWithText("Create New Request"); - selenium.waitForPageToLoad("30000"); - selenium.select("destinationSite", "label=Aurum Health KOSH Lab, Orkney, South Africa (Repository)"); - selenium.type("input0", "Assay Plan"); - selenium.type("input2", "Comments"); - selenium.type("input1", "Shipping"); + clickLinkWithText("Plasma, Unknown Processing"); + checkCheckbox(".toggle"); + clickMenuButton("Request Options", "Create New Request"); + selectOptionByText("destinationSite", "Aurum Health KOSH Lab, Orkney, South Africa (Repository)"); + setFormElement("input0", "Assay Plan"); + setFormElement("input2", "Comments"); + setFormElement("input1", "Shipping"); clickNavButton("Create and View Details"); - selenium.waitForPageToLoad("30000"); assertTextPresent("Please provide all required input."); - selenium.type("input3", "sample last one input"); + setFormElement("input3", "sample last one input"); clickNavButton("Create and View Details"); - selenium.waitForPageToLoad("30000"); assertTextPresent("sample last one input"); assertTextPresent("IRB"); assertTextPresent("KCMC, Moshi, Tanzania"); assertTextPresent("Originating IRB Approval"); assertTextPresent("Contract Lab Services, Johannesburg, South Africa (Repository)"); assertTextPresent("Providing IRB Approval"); - assertTextPresent("Aurum Health KOSH Lab, Orkney, South Africa (Repository)"); + assertTextPresent(DESTINATION_SITE); assertTextPresent("Receiving IRB Approval"); assertTextPresent("SLG"); assertTextPresent("SLG Approval"); @@ -179,31 +172,25 @@ protected void doTestSteps() // verify that the swab specimen isn't present yet assertTextNotPresent("DAA07YGW-01"); assertTextNotPresent("Complete"); - selenium.click("link=My Study Study"); - selenium.waitForPageToLoad("30000"); + clickLinkWithText(STUDY_NAME); // add additional specimens - selenium.click("link=Swab"); - selenium.waitForPageToLoad("30000"); - selenium.click(".toggle"); - clickNavButton("Request Options", 0); - clickLinkWithText("Add To Existing Request", false); - sleep(15000); + clickLinkWithText("Swab"); + checkCheckbox(".toggle"); + clickMenuButtonAndContinue("Request Options", "Add To Existing Request"); + ExtHelper.waitForExtDialog(this, "Request Vial", WAIT_FOR_JAVASCRIPT); clickNavButton("Add 8 Vials to Request", 0); - sleep(15000); + ExtHelper.waitForExtDialog(this, "Success", WAIT_FOR_JAVASCRIPT * 5); clickNavButton("OK", 0); - sleep(15000); - clickNavButton("Request Options", 0); - clickLinkWithText("View Existing Requests"); + clickMenuButton("Request Options", "View Existing Requests"); clickNavButton("Details"); - selenium.waitForPageToLoad("30000"); assertTextPresent("sample last one input"); assertTextPresent("IRB"); assertTextPresent("KCMC, Moshi, Tanzania"); assertTextPresent("Originating IRB Approval"); assertTextPresent("Contract Lab Services, Johannesburg, South Africa (Repository)"); assertTextPresent("Providing IRB Approval"); - assertTextPresent("Aurum Health KOSH Lab, Orkney, South Africa (Repository)"); + assertTextPresent(DESTINATION_SITE); assertTextPresent("Receiving IRB Approval"); assertTextPresent("SLG"); assertTextPresent("SLG Approval"); @@ -219,44 +206,64 @@ protected void doTestSteps() assertTextPresent("New Request"); // modify request - selenium.select("newActor", "label=SLG"); - selenium.type("newDescription", "Other SLG Approval"); + selectOptionByText("newActor", "SLG"); + setFormElement("newDescription", "Other SLG Approval"); clickNavButton("Add Requirement"); - selenium.waitForPageToLoad("30000"); - selenium.click("//a[contains(@href, 'manageRequirement.view')]"); - selenium.waitForPageToLoad("30000"); - selenium.click("complete"); + clickLinkWithText("Details"); + checkCheckbox("complete"); + checkCheckbox("notificationIdPairs"); + checkCheckbox("notificationIdPairs", 1); clickNavButton("Save Changes and Send Notifications"); - selenium.waitForPageToLoad("30000"); assertTextPresent("Complete"); // verify views - selenium.click("link=View History"); - selenium.waitForPageToLoad("30000"); + clickLinkWithText("View History"); assertTextPresent("Request submitted for processing."); - selenium.click("link=View Request"); - selenium.waitForPageToLoad("30000"); - selenium.click("link=Originating Location Specimen Lists"); - selenium.waitForPageToLoad("30000"); + assertTextPresent("Notification Sent", 2); assertTextPresent(USER1); assertTextPresent(USER2); + clickLinkWithText("View Request"); + clickLinkWithText("Originating Location Specimen Lists"); assertTextPresent("KCMC, Moshi, Tanzania"); - clickNavButton("Cancel"); - selenium.click("link=Providing Location Specimen Lists"); - selenium.waitForPageToLoad("30000"); + checkCheckbox("notify"); + checkCheckbox("notify", 4); + checkCheckbox("sendXls"); + checkCheckbox("sendTsv"); + clickNavButton("Send Email"); + clickLinkWithText("Providing Location Specimen Lists"); assertTextPresent("Contract Lab Services, Johannesburg, South Africa (Repository)"); clickNavButton("Cancel"); // cancel request - selenium.click("link=Update Status"); - selenium.waitForPageToLoad("30000"); - selenium.select("status", "label=Not Yet Submitted"); + clickLinkWithText("Update Status"); + selectOptionByText("status", "Not Yet Submitted"); clickNavButton("Save Changes and Send Notifications"); clickNavButton("Cancel Request"); assertTrue(selenium.getConfirmation().matches("^Canceling will permanently delete this pending request\\. Continue[\\s\\S]$")); assertTextPresent("No data to show."); - clickLinkWithText("My Study Study"); + clickLinkWithText(STUDY_NAME); clickLinkWithText("Swab"); - selenium.click(".toggle"); - clickNavButton("Request Options", 0); - clickLinkWithText("Create New Request"); + checkCheckbox(".toggle"); + clickMenuButton("Request Options", "Create New Request"); clickNavButton("Cancel"); + + log("Check notification emails"); + clickLinkWithText(PROJECT_NAME); + assertTextPresent("Specimen Request Notification", 4); + assertTextPresent(USER1, 2); + assertTextPresent(USER2, 2); + + log("Check for correct data in notification emails"); + if ( getTableCellText("dataregion_EmailRecord", 3, 0).equals(USER1)) + { + clickLinkContainingText("Specimen Request Notification", 1, false); + assertTextNotPresent("Swab"); + clickLinkContainingText("Specimen Request Notification", false); + assertTextPresent("Swab"); + } + else + { + clickLinkContainingText("Specimen Request Notification", false); + assertTextNotPresent("Swab"); + clickLinkContainingText("Specimen Request Notification", 1, false); + assertTextPresent("Swab"); + } } } From 089608aef9d7fbe5d1f452468043c64c20434ec2 Mon Sep 17 00:00:00 2001 From: Trey Chadick Date: Thu, 1 Apr 2010 18:12:32 +0000 Subject: [PATCH 026/494] test fix SVN r13411 |2010-04-01 18:12:32 +0000 --- src/org/labkey/test/bvt/SpecimenTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/org/labkey/test/bvt/SpecimenTest.java b/src/org/labkey/test/bvt/SpecimenTest.java index b7d9b335..47750776 100644 --- a/src/org/labkey/test/bvt/SpecimenTest.java +++ b/src/org/labkey/test/bvt/SpecimenTest.java @@ -251,7 +251,7 @@ protected void doTestSteps() assertTextPresent(USER2, 2); log("Check for correct data in notification emails"); - if ( getTableCellText("dataregion_EmailRecord", 3, 0).equals(USER1)) + if ( getTableCellText("dataregion_EmailRecord", 3, 1).equals(USER1)) { clickLinkContainingText("Specimen Request Notification", 1, false); assertTextNotPresent("Swab"); From a05f46e0b7fc1a9d4fd993ae55eab2dccd7c7e17 Mon Sep 17 00:00:00 2001 From: Trey Chadick Date: Mon, 5 Apr 2010 19:20:18 +0000 Subject: [PATCH 027/494] bvt fix SVN r13450 |2010-04-05 19:20:18 +0000 --- src/org/labkey/test/bvt/SpecimenTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/org/labkey/test/bvt/SpecimenTest.java b/src/org/labkey/test/bvt/SpecimenTest.java index 47750776..b7d9b335 100644 --- a/src/org/labkey/test/bvt/SpecimenTest.java +++ b/src/org/labkey/test/bvt/SpecimenTest.java @@ -251,7 +251,7 @@ protected void doTestSteps() assertTextPresent(USER2, 2); log("Check for correct data in notification emails"); - if ( getTableCellText("dataregion_EmailRecord", 3, 1).equals(USER1)) + if ( getTableCellText("dataregion_EmailRecord", 3, 0).equals(USER1)) { clickLinkContainingText("Specimen Request Notification", 1, false); assertTextNotPresent("Swab"); From c3f5b547329aae7f3c69a1fcca816a79357d8046 Mon Sep 17 00:00:00 2001 From: Trey Chadick Date: Wed, 7 Apr 2010 18:19:45 +0000 Subject: [PATCH 028/494] Test fix. Specimen order is not deterministic. SVN r13473 |2010-04-07 18:19:45 +0000 --- src/org/labkey/test/bvt/SpecimenTest.java | 24 +++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/src/org/labkey/test/bvt/SpecimenTest.java b/src/org/labkey/test/bvt/SpecimenTest.java index b7d9b335..882b79cf 100644 --- a/src/org/labkey/test/bvt/SpecimenTest.java +++ b/src/org/labkey/test/bvt/SpecimenTest.java @@ -39,6 +39,8 @@ public class SpecimenTest extends BaseSeleniumWebTest private static final String DESTINATION_SITE = "Aurum Health KOSH Lab, Orkney, South Africa (Repository)"; private static final String USER1 = "user1@specimen.test"; private static final String USER2 = "user2@specimen.test"; + private String SPECIMEN1 = ""; + private String SPECIMEN2 = ""; public String getAssociatedModuleDirectory() @@ -110,7 +112,7 @@ protected void doTestSteps() selectOptionByText("newPerSite", "One Per Study"); clickNavButton("Save"); clickLinkWithText("Update Members"); - setText("names", USER2); + setText("names", USER1); uncheckCheckbox("sendEmail"); clickNavButton("Update Members"); setFormElement("newLabel", "IRB"); @@ -118,7 +120,7 @@ protected void doTestSteps() clickNavButton("Save"); clickLinkWithText("Update Members", 1); clickLinkWithText(DESTINATION_SITE); - setText("names", USER1); + setText("names", USER2); uncheckCheckbox("sendEmail"); clickLinkWithText("Update Members"); clickLinkWithText(STUDY_NAME); @@ -219,12 +221,16 @@ protected void doTestSteps() // verify views clickLinkWithText("View History"); assertTextPresent("Request submitted for processing."); - assertTextPresent("Notification Sent", 2); assertTextPresent(USER1); assertTextPresent(USER2); + assertTextPresent("Notification Sent", 2); + assertTextPresent(USER1); + assertTextPresent(USER2); clickLinkWithText("View Request"); clickLinkWithText("Originating Location Specimen Lists"); assertTextPresent("KCMC, Moshi, Tanzania"); checkCheckbox("notify"); + SPECIMEN1 = getText(Locator.xpath("//tr[@class = 'labkey-alternate-row']/td[3]//td")); checkCheckbox("notify", 4); + SPECIMEN2 = getText(Locator.xpath("//tr[@class = 'labkey-row']/td[3]//td")); checkCheckbox("sendXls"); checkCheckbox("sendTsv"); clickNavButton("Send Email"); @@ -253,17 +259,19 @@ protected void doTestSteps() log("Check for correct data in notification emails"); if ( getTableCellText("dataregion_EmailRecord", 3, 0).equals(USER1)) { - clickLinkContainingText("Specimen Request Notification", 1, false); - assertTextNotPresent("Swab"); clickLinkContainingText("Specimen Request Notification", false); - assertTextPresent("Swab"); + assertTextPresent(SPECIMEN1); + assertTextNotPresent(SPECIMEN2); + clickLinkContainingText("Specimen Request Notification", 1, false); + assertTextPresent(SPECIMEN2); } else { clickLinkContainingText("Specimen Request Notification", false); - assertTextNotPresent("Swab"); + assertTextPresent(SPECIMEN2); + assertTextNotPresent(SPECIMEN1); clickLinkContainingText("Specimen Request Notification", 1, false); - assertTextPresent("Swab"); + assertTextPresent(SPECIMEN1); } } } From f2198d6fc3a7722db3f64aa0a232e9190a48c3d6 Mon Sep 17 00:00:00 2001 From: Trey Chadick Date: Mon, 12 Apr 2010 17:13:34 +0000 Subject: [PATCH 029/494] Hack to fix specimen test SVN r13494 |2010-04-12 17:13:34 +0000 --- src/org/labkey/test/bvt/SpecimenTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/org/labkey/test/bvt/SpecimenTest.java b/src/org/labkey/test/bvt/SpecimenTest.java index 882b79cf..0a56508b 100644 --- a/src/org/labkey/test/bvt/SpecimenTest.java +++ b/src/org/labkey/test/bvt/SpecimenTest.java @@ -181,6 +181,7 @@ protected void doTestSteps() checkCheckbox(".toggle"); clickMenuButtonAndContinue("Request Options", "Add To Existing Request"); ExtHelper.waitForExtDialog(this, "Request Vial", WAIT_FOR_JAVASCRIPT); + sleep(10000); //TODO: Determine which specific element to wait for. clickNavButton("Add 8 Vials to Request", 0); ExtHelper.waitForExtDialog(this, "Success", WAIT_FOR_JAVASCRIPT * 5); clickNavButton("OK", 0); From aa45530b120cef53cee108f1bca8dffd62dda5ad Mon Sep 17 00:00:00 2001 From: Trey Chadick Date: Tue, 4 May 2010 23:35:22 +0000 Subject: [PATCH 030/494] wait for page to load after dismissing/verifying confirmation dialogs SVN r13657 |2010-05-04 23:35:22 +0000 --- src/org/labkey/test/bvt/SpecimenTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/org/labkey/test/bvt/SpecimenTest.java b/src/org/labkey/test/bvt/SpecimenTest.java index 0a56508b..56138f14 100644 --- a/src/org/labkey/test/bvt/SpecimenTest.java +++ b/src/org/labkey/test/bvt/SpecimenTest.java @@ -203,8 +203,8 @@ protected void doTestSteps() // submit request assertTextPresent("Not Yet Submitted"); assertTextNotPresent("New Request"); - clickNavButton("Submit Request"); - assertTrue(selenium.getConfirmation().matches("^Once a request is submitted, its specimen list may no longer be modified\\. Continue[\\s\\S]$")); + clickNavButton("Submit Request", 0); + assertTrue(getConfirmationAndWait().matches("^Once a request is submitted, its specimen list may no longer be modified\\. Continue[\\s\\S]$")); assertTextNotPresent("Not Yet Submitted"); assertTextPresent("New Request"); @@ -243,7 +243,7 @@ protected void doTestSteps() selectOptionByText("status", "Not Yet Submitted"); clickNavButton("Save Changes and Send Notifications"); clickNavButton("Cancel Request"); - assertTrue(selenium.getConfirmation().matches("^Canceling will permanently delete this pending request\\. Continue[\\s\\S]$")); + assertTrue(getConfirmationAndWait().matches("^Canceling will permanently delete this pending request\\. Continue[\\s\\S]$")); assertTextPresent("No data to show."); clickLinkWithText(STUDY_NAME); clickLinkWithText("Swab"); From 2c063e3c9755daec1edd189e80b921cfc1c8a544 Mon Sep 17 00:00:00 2001 From: Trey Chadick Date: Wed, 5 May 2010 21:56:35 +0000 Subject: [PATCH 031/494] BVT fix SVN r13663 |2010-05-05 21:56:35 +0000 --- src/org/labkey/test/bvt/SpecimenTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/org/labkey/test/bvt/SpecimenTest.java b/src/org/labkey/test/bvt/SpecimenTest.java index 56138f14..62516728 100644 --- a/src/org/labkey/test/bvt/SpecimenTest.java +++ b/src/org/labkey/test/bvt/SpecimenTest.java @@ -242,7 +242,7 @@ protected void doTestSteps() clickLinkWithText("Update Status"); selectOptionByText("status", "Not Yet Submitted"); clickNavButton("Save Changes and Send Notifications"); - clickNavButton("Cancel Request"); + clickNavButton("Cancel Request", 0); assertTrue(getConfirmationAndWait().matches("^Canceling will permanently delete this pending request\\. Continue[\\s\\S]$")); assertTextPresent("No data to show."); clickLinkWithText(STUDY_NAME); From ee3baaf95bc542721de866340c13e8570204dff0 Mon Sep 17 00:00:00 2001 From: Karl Lum Date: Thu, 6 May 2010 00:46:04 +0000 Subject: [PATCH 032/494] BVT test fixes SVN r13667 |2010-05-06 00:46:04 +0000 --- src/org/labkey/test/bvt/SpecimenTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/org/labkey/test/bvt/SpecimenTest.java b/src/org/labkey/test/bvt/SpecimenTest.java index 62516728..784372c6 100644 --- a/src/org/labkey/test/bvt/SpecimenTest.java +++ b/src/org/labkey/test/bvt/SpecimenTest.java @@ -84,7 +84,7 @@ protected void doTestSteps() clickNavButton("Create Study"); clickLinkWithText("My Study"); - clickLinkWithText("Data Pipeline"); + clickLinkWithText("Manage Files"); clickNavButton("Setup"); setPipelineRoot(_studyDataRoot); clickLinkWithText("Pipeline"); From 7ac2dda8225aed70d1dfcdee20c310cdd898428e Mon Sep 17 00:00:00 2001 From: Karl Lum Date: Mon, 24 May 2010 19:18:39 +0000 Subject: [PATCH 033/494] 10244 : file management: Email contains # of files, folder name link and preferences link Added a link in the notification email where the user can update their email preferences. Fix BVT tests broken by pipeline setup changes. SVN r13834 |2010-05-24 19:18:39 +0000 --- src/org/labkey/test/bvt/SpecimenTest.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/org/labkey/test/bvt/SpecimenTest.java b/src/org/labkey/test/bvt/SpecimenTest.java index 784372c6..4d98c445 100644 --- a/src/org/labkey/test/bvt/SpecimenTest.java +++ b/src/org/labkey/test/bvt/SpecimenTest.java @@ -84,10 +84,9 @@ protected void doTestSteps() clickNavButton("Create Study"); clickLinkWithText("My Study"); - clickLinkWithText("Manage Files"); - clickNavButton("Setup"); setPipelineRoot(_studyDataRoot); - clickLinkWithText("Pipeline"); + clickLinkWithText("My Study"); + clickLinkWithText("Manage Files"); SpecimenImporter importer = new SpecimenImporter(new File(_studyDataRoot), new File(getLabKeyRoot(), SPECIMEN_ARCHIVE), new File(getLabKeyRoot(), SPECIMEN_TEMP_DIR), FOLDER_NAME, 1); importer.importAndWaitForComplete(); From ed91131b38fd58641db8a7dc4ee413ac2a9ad60f Mon Sep 17 00:00:00 2001 From: Josh Eckels Date: Thu, 5 Aug 2010 15:41:06 +0000 Subject: [PATCH 034/494] SpecimentTest fix - update with new expected counts based on Dumbster changes SVN r14285 |2010-08-05 15:41:06 +0000 --- src/org/labkey/test/bvt/SpecimenTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/org/labkey/test/bvt/SpecimenTest.java b/src/org/labkey/test/bvt/SpecimenTest.java index 4d98c445..3caa70fe 100644 --- a/src/org/labkey/test/bvt/SpecimenTest.java +++ b/src/org/labkey/test/bvt/SpecimenTest.java @@ -252,9 +252,9 @@ protected void doTestSteps() log("Check notification emails"); clickLinkWithText(PROJECT_NAME); - assertTextPresent("Specimen Request Notification", 4); - assertTextPresent(USER1, 2); - assertTextPresent(USER2, 2); + assertTextPresent("Specimen Request Notification", 8); + assertTextPresent(USER1, 4); + assertTextPresent(USER2, 4); log("Check for correct data in notification emails"); if ( getTableCellText("dataregion_EmailRecord", 3, 0).equals(USER1)) From a515010fe7397f9a082733ca133b035725dae573 Mon Sep 17 00:00:00 2001 From: Kevin Krouse Date: Wed, 15 Sep 2010 01:41:16 +0000 Subject: [PATCH 035/494] - include reagent module in default build - initial checkin of reagent test - test's associated module dir is relative to the project root SVN r14529 |2010-09-15 01:41:16 +0000 --- src/org/labkey/test/bvt/SpecimenTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/org/labkey/test/bvt/SpecimenTest.java b/src/org/labkey/test/bvt/SpecimenTest.java index 3caa70fe..e57cccf2 100644 --- a/src/org/labkey/test/bvt/SpecimenTest.java +++ b/src/org/labkey/test/bvt/SpecimenTest.java @@ -45,7 +45,7 @@ public class SpecimenTest extends BaseSeleniumWebTest public String getAssociatedModuleDirectory() { - return "study"; + return "server/modules/study"; } @Override From 0a98e51fb0449c0b7c012e6c3d415b31e47d7c9d Mon Sep 17 00:00:00 2001 From: Trey Chadick Date: Tue, 12 Oct 2010 21:51:36 +0000 Subject: [PATCH 036/494] Add custom requestability rules test to SpecimenTest Add wait to ext menu helper. Fix string in Specimen tooltip. SVN r14853 |2010-10-12 21:51:36 +0000 --- src/org/labkey/test/bvt/SpecimenTest.java | 45 +++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/src/org/labkey/test/bvt/SpecimenTest.java b/src/org/labkey/test/bvt/SpecimenTest.java index e57cccf2..15708d1f 100644 --- a/src/org/labkey/test/bvt/SpecimenTest.java +++ b/src/org/labkey/test/bvt/SpecimenTest.java @@ -39,6 +39,8 @@ public class SpecimenTest extends BaseSeleniumWebTest private static final String DESTINATION_SITE = "Aurum Health KOSH Lab, Orkney, South Africa (Repository)"; private static final String USER1 = "user1@specimen.test"; private static final String USER2 = "user2@specimen.test"; + private static final String REQUESTABILITY_QUERY = "RequestabilityRule"; + private static final String UNREQUESTABLE_SAMPLE = "BAA07XNP-02"; private String SPECIMEN1 = ""; private String SPECIMEN2 = ""; @@ -82,6 +84,44 @@ protected void doTestSteps() clickNavButton("Create Study"); click(Locator.radioButtonByNameAndValue("simpleRepository", "false")); clickNavButton("Create Study"); + + // Create custom query to test requestability rules. + goToSchemaBrowser(); + selectQuery("study", "SpecimenDetail"); + clickButton("Create New Query"); + setFormElement("ff_newQueryName", REQUESTABILITY_QUERY); + clickLinkWithText("Create and Edit Source"); + toggleQueryEditors(); + setFormElement("ff_queryText", + "SELECT \n" + + "SpecimenDetail.GlobalUniqueId AS GlobalUniqueId\n" + + "FROM SpecimenDetail\n" + + "WHERE SpecimenDetail.GlobalUniqueId='" + UNREQUESTABLE_SAMPLE + "'"); + clickNavButton("Save"); + + clickLinkWithText(STUDY_NAME); + clickLinkWithText("Manage Study"); + clickLinkWithText("Manage Requestability Rules"); + // Verify that LOCKED_IN_REQUEST is the last rule + assertElementPresent(Locator.xpath("//div[contains(@class, 'x-grid3-row-last')]//div[text()='Locked In Request Check']")); + mouseDown(Locator.xpath("//div[contains(@class, 'x-grid3-row-last')]//div[text()='Locked In Request Check']")); + // Verify that LOCKED_IN_REQUEST rule cannot be moved or deleted + assertElementPresent(Locator.xpath("//table[@id='btn_deleteEngine' and contains(@class, 'x-item-disabled')]")); + assertElementPresent(Locator.xpath("//table[@id='btn_moveUp' and contains(@class, 'x-item-disabled')]")); + assertElementPresent(Locator.xpath("//table[@id='btn_moveDown' and contains(@class, 'x-item-disabled')]")); + mouseDown(Locator.xpath("//div[contains(@class, 'x-grid3-col-numberer') and text()='2']")); + assertElementPresent(Locator.xpath("//table[@id='btn_deleteEngine' and not(contains(@class, 'x-item-disabled'))]")); + assertElementPresent(Locator.xpath("//table[@id='btn_moveUp' and not(contains(@class, 'x-item-disabled'))]")); + assertElementPresent(Locator.xpath("//table[@id='btn_moveDown' and contains(@class, 'x-item-disabled')]")); + clickButton("Add Rule", 0); + click(Locator.menuItem("Custom Query")); + ExtHelper.selectComboBoxItem(this, Locator.xpath("//div[@id='x-form-el-userQuery_schema']"), "study" ); + ExtHelper.selectComboBoxItem(this, Locator.xpath("//div[@id='x-form-el-userQuery_query']"), REQUESTABILITY_QUERY ); + ExtHelper.selectComboBoxItem(this, Locator.xpath("//div[@id='x-form-el-userQuery_action']"), "Unavailable" ); + clickButton("Submit",0); + clickButton("Save"); + + clickLinkWithText("My Study"); setPipelineRoot(_studyDataRoot); @@ -149,6 +189,10 @@ protected void doTestSteps() // create request clickLinkWithText("Plasma, Unknown Processing"); + // Verify unavailable sample + assertElementPresent(Locator.xpath("//input[@id='check_" + UNREQUESTABLE_SAMPLE + "' and @disabled]")); + assertElementPresent(Locator.xpath("//input[@id='check_" + UNREQUESTABLE_SAMPLE + "']/../a[contains(@onmouseover, 'an administrator has overridden its availability status.')]")); + assertElementPresent(Locator.xpath("//input[@id='check_" + UNREQUESTABLE_SAMPLE + "']/../../td[contains(text(), 'Unavailable based on Custom Query: study." + REQUESTABILITY_QUERY + ".')]")); checkCheckbox(".toggle"); clickMenuButton("Request Options", "Create New Request"); selectOptionByText("destinationSite", "Aurum Health KOSH Lab, Orkney, South Africa (Repository)"); @@ -170,6 +214,7 @@ protected void doTestSteps() assertTextPresent("SLG"); assertTextPresent("SLG Approval"); assertTextPresent("BAA07XNP-01"); + assertTextNotPresent(UNREQUESTABLE_SAMPLE); // verify that the swab specimen isn't present yet assertTextNotPresent("DAA07YGW-01"); assertTextNotPresent("Complete"); From ac971fd9219c165bd2acd6ef37004cb869e050bb Mon Sep 17 00:00:00 2001 From: Kevin Krouse Date: Fri, 22 Oct 2010 00:23:55 +0000 Subject: [PATCH 037/494] 11029 : Selected fields/sorts/filters column gets pushed too far right on wide tables - move the ribbon panel and message bar into the header/footer table as separate rows - resize the entire header/footer table instead of just the pagination text - remove excess padding from header/footer - float the filter item's value textbox so on wide displays the filter op combo and value textbox are on the same line SVN r14971 |2010-10-22 00:23:55 +0000 --- src/org/labkey/test/bvt/SpecimenTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/org/labkey/test/bvt/SpecimenTest.java b/src/org/labkey/test/bvt/SpecimenTest.java index 15708d1f..641fd5a8 100644 --- a/src/org/labkey/test/bvt/SpecimenTest.java +++ b/src/org/labkey/test/bvt/SpecimenTest.java @@ -302,7 +302,7 @@ protected void doTestSteps() assertTextPresent(USER2, 4); log("Check for correct data in notification emails"); - if ( getTableCellText("dataregion_EmailRecord", 3, 0).equals(USER1)) + if ( getTableCellText("dataregion_EmailRecord", 2, 0).equals(USER1)) { clickLinkContainingText("Specimen Request Notification", false); assertTextPresent(SPECIMEN1); From aa174fe1b282a69e43ef096a91977389f9f78f5f Mon Sep 17 00:00:00 2001 From: Kevin Krouse Date: Thu, 18 Nov 2010 22:24:19 +0000 Subject: [PATCH 038/494] Merge 10.3 changes from 15097-15187 SVN r15188 |2010-11-18 22:24:19 +0000 --- src/org/labkey/test/bvt/SpecimenTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/org/labkey/test/bvt/SpecimenTest.java b/src/org/labkey/test/bvt/SpecimenTest.java index 641fd5a8..e2f93751 100644 --- a/src/org/labkey/test/bvt/SpecimenTest.java +++ b/src/org/labkey/test/bvt/SpecimenTest.java @@ -283,7 +283,7 @@ protected void doTestSteps() assertTextPresent("Contract Lab Services, Johannesburg, South Africa (Repository)"); clickNavButton("Cancel"); // cancel request - clickLinkWithText("Update Status"); + clickLinkWithText("Update Request"); selectOptionByText("status", "Not Yet Submitted"); clickNavButton("Save Changes and Send Notifications"); clickNavButton("Cancel Request", 0); From 9a8f3f8106e94a49336a51efe89b8be6a294603c Mon Sep 17 00:00:00 2001 From: Kevin Krouse Date: Thu, 2 Dec 2010 00:18:11 +0000 Subject: [PATCH 039/494] Merge 10.3 changes from 15190-15231 SVN r15236 |2010-12-02 00:18:11 +0000 --- src/org/labkey/test/bvt/SpecimenTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/org/labkey/test/bvt/SpecimenTest.java b/src/org/labkey/test/bvt/SpecimenTest.java index e2f93751..c03617bb 100644 --- a/src/org/labkey/test/bvt/SpecimenTest.java +++ b/src/org/labkey/test/bvt/SpecimenTest.java @@ -191,8 +191,8 @@ protected void doTestSteps() clickLinkWithText("Plasma, Unknown Processing"); // Verify unavailable sample assertElementPresent(Locator.xpath("//input[@id='check_" + UNREQUESTABLE_SAMPLE + "' and @disabled]")); - assertElementPresent(Locator.xpath("//input[@id='check_" + UNREQUESTABLE_SAMPLE + "']/../a[contains(@onmouseover, 'an administrator has overridden its availability status.')]")); - assertElementPresent(Locator.xpath("//input[@id='check_" + UNREQUESTABLE_SAMPLE + "']/../../td[contains(text(), 'Unavailable based on Custom Query: study." + REQUESTABILITY_QUERY + ".')]")); + assertElementPresent(Locator.xpath("//input[@id='check_" + UNREQUESTABLE_SAMPLE + "']/../../td[contains(text(), 'This vial is unavailable because it was found in the set called \"" + REQUESTABILITY_QUERY + "\".')]")); + assertElementPresent(Locator.xpath("//input[@id='check_" + UNREQUESTABLE_SAMPLE + "']/../a[contains(@onmouseover, 'This vial is unavailable because it was found in the set called \\\"" + REQUESTABILITY_QUERY + "\\\".')]")); checkCheckbox(".toggle"); clickMenuButton("Request Options", "Create New Request"); selectOptionByText("destinationSite", "Aurum Health KOSH Lab, Orkney, South Africa (Repository)"); From a05b7b389397f79c562f991483db89ba4051b09d Mon Sep 17 00:00:00 2001 From: Trey Chadick Date: Fri, 25 Feb 2011 21:07:15 +0000 Subject: [PATCH 040/494] 11101: Modify tests to visit dumbster module rather than adding a mail record web part SVN r15658 |2011-02-25 21:07:15 +0000 --- src/org/labkey/test/bvt/SpecimenTest.java | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/org/labkey/test/bvt/SpecimenTest.java b/src/org/labkey/test/bvt/SpecimenTest.java index c03617bb..88ea7727 100644 --- a/src/org/labkey/test/bvt/SpecimenTest.java +++ b/src/org/labkey/test/bvt/SpecimenTest.java @@ -75,10 +75,8 @@ protected void doTestSteps() _studyDataRoot = getLabKeyRoot() + "/sampledata/study"; createProject(PROJECT_NAME); - enableModule(PROJECT_NAME, "Dumbster"); - addWebPart("Mail Record"); - uncheckCheckbox("emailRecordOn"); - checkCheckbox("emailRecordOn"); + + enableEmailRecorder(); createSubfolder(PROJECT_NAME, PROJECT_NAME, FOLDER_NAME, "Study", null); clickNavButton("Create Study"); @@ -296,7 +294,7 @@ protected void doTestSteps() clickNavButton("Cancel"); log("Check notification emails"); - clickLinkWithText(PROJECT_NAME); + goToModule("Dumbster"); assertTextPresent("Specimen Request Notification", 8); assertTextPresent(USER1, 4); assertTextPresent(USER2, 4); From 518347221cee62fd11846722cc206ed4236f4207 Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Mon, 28 Feb 2011 17:09:13 +0000 Subject: [PATCH 041/494] Update licenses SVN r15683 |2011-02-28 17:09:13 +0000 --- src/org/labkey/test/bvt/SpecimenTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/org/labkey/test/bvt/SpecimenTest.java b/src/org/labkey/test/bvt/SpecimenTest.java index 88ea7727..a204e490 100644 --- a/src/org/labkey/test/bvt/SpecimenTest.java +++ b/src/org/labkey/test/bvt/SpecimenTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007-2010 LabKey Corporation + * Copyright (c) 2007-2011 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. From 2c1af18670f9d77094a321dff39ad3447f32038d Mon Sep 17 00:00:00 2001 From: Nick Kerr Date: Thu, 9 Jun 2011 18:42:59 +0000 Subject: [PATCH 042/494] Revamped SQL Editor - Ajax save of source and metadata. - Ajax data view with metadata applied. - QueryWebPart accepts 'metadata' to be applied. - QueryWebPart 'returnError' now receives 'html'/'json'. SVN r16415 |2011-06-09 18:42:59 +0000 --- src/org/labkey/test/bvt/SpecimenTest.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/org/labkey/test/bvt/SpecimenTest.java b/src/org/labkey/test/bvt/SpecimenTest.java index a204e490..ba55ca74 100644 --- a/src/org/labkey/test/bvt/SpecimenTest.java +++ b/src/org/labkey/test/bvt/SpecimenTest.java @@ -89,13 +89,14 @@ protected void doTestSteps() clickButton("Create New Query"); setFormElement("ff_newQueryName", REQUESTABILITY_QUERY); clickLinkWithText("Create and Edit Source"); - toggleQueryEditors(); - setFormElement("ff_queryText", + toggleSQLQueryEditor(); + setFormElement("queryText", "SELECT \n" + "SpecimenDetail.GlobalUniqueId AS GlobalUniqueId\n" + "FROM SpecimenDetail\n" + "WHERE SpecimenDetail.GlobalUniqueId='" + UNREQUESTABLE_SAMPLE + "'"); - clickNavButton("Save"); + clickButton("Save", 0); + waitForText("Saved", WAIT_FOR_JAVASCRIPT); clickLinkWithText(STUDY_NAME); clickLinkWithText("Manage Study"); From 8dda366747ce822c17c1b308e2b9264b8b039479 Mon Sep 17 00:00:00 2001 From: Britt Piehler Date: Fri, 17 Jun 2011 22:25:01 +0000 Subject: [PATCH 043/494] Addition of TubeType column to specimen file format, importer, and tests. Also fixed small bug in a luminex SQL script that was preventing upgrade on SQL server. SVN r16493 |2011-06-17 22:25:01 +0000 --- src/org/labkey/test/bvt/SpecimenTest.java | 37 +++++++++++++++++------ 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/src/org/labkey/test/bvt/SpecimenTest.java b/src/org/labkey/test/bvt/SpecimenTest.java index ba55ca74..c36ed6e7 100644 --- a/src/org/labkey/test/bvt/SpecimenTest.java +++ b/src/org/labkey/test/bvt/SpecimenTest.java @@ -41,8 +41,6 @@ public class SpecimenTest extends BaseSeleniumWebTest private static final String USER2 = "user2@specimen.test"; private static final String REQUESTABILITY_QUERY = "RequestabilityRule"; private static final String UNREQUESTABLE_SAMPLE = "BAA07XNP-02"; - private String SPECIMEN1 = ""; - private String SPECIMEN2 = ""; public String getAssociatedModuleDirectory() @@ -130,6 +128,25 @@ protected void doTestSteps() SpecimenImporter importer = new SpecimenImporter(new File(_studyDataRoot), new File(getLabKeyRoot(), SPECIMEN_ARCHIVE), new File(getLabKeyRoot(), SPECIMEN_TEMP_DIR), FOLDER_NAME, 1); importer.importAndWaitForComplete(); + // Field check for Tube Type column (including conflict) + clickLinkWithText(STUDY_NAME); + clickLinkWithText("By Vial"); + setFilter("SpecimenDetail", "PrimaryType", "Is Blank"); + // Verify that there's only one vial of unknown type: + assertLinkPresentWithTextCount("[history]", 1); + // There's a conflict in TubeType for this vial's events; verify that no TubeType is populated at the vial level + assertTextNotPresent("Cryovial"); + clickLinkWithText("[history]"); + // This vial has three events, each of which list a different tube type: + assertTextPresent("15ml Cryovial"); + assertTextPresent("20ml Cryovial"); + assertTextPresent("25ml Cryovial"); + clickLinkWithText("Specimen Overview"); + clickLinkWithText("Tear Flo Strips"); + // For these three vials, there should be no conflict in TubeType, so we should see the text once for each of three vials: + assertLinkPresentWithTextCount("[history]", 3); + assertTextPresent("15ml Cryovial", 3); + // specimen management setup clickLinkWithText(STUDY_NAME); clickLinkWithText("Manage Statuses"); @@ -272,9 +289,9 @@ protected void doTestSteps() clickLinkWithText("Originating Location Specimen Lists"); assertTextPresent("KCMC, Moshi, Tanzania"); checkCheckbox("notify"); - SPECIMEN1 = getText(Locator.xpath("//tr[@class = 'labkey-alternate-row']/td[3]//td")); + String specimen1 = getText(Locator.xpath("//tr[@class = 'labkey-alternate-row']/td[3]//td")); checkCheckbox("notify", 4); - SPECIMEN2 = getText(Locator.xpath("//tr[@class = 'labkey-row']/td[3]//td")); + String specimen2 = getText(Locator.xpath("//tr[@class = 'labkey-row']/td[3]//td")); checkCheckbox("sendXls"); checkCheckbox("sendTsv"); clickNavButton("Send Email"); @@ -304,18 +321,18 @@ protected void doTestSteps() if ( getTableCellText("dataregion_EmailRecord", 2, 0).equals(USER1)) { clickLinkContainingText("Specimen Request Notification", false); - assertTextPresent(SPECIMEN1); - assertTextNotPresent(SPECIMEN2); + assertTextPresent(specimen1); + assertTextNotPresent(specimen2); clickLinkContainingText("Specimen Request Notification", 1, false); - assertTextPresent(SPECIMEN2); + assertTextPresent(specimen2); } else { clickLinkContainingText("Specimen Request Notification", false); - assertTextPresent(SPECIMEN2); - assertTextNotPresent(SPECIMEN1); + assertTextPresent(specimen2); + assertTextNotPresent(specimen1); clickLinkContainingText("Specimen Request Notification", 1, false); - assertTextPresent(SPECIMEN1); + assertTextPresent(specimen1); } } } From 8a42b901bd7c5883dae6e22d2a1e36280798911f Mon Sep 17 00:00:00 2001 From: Nick Kerr Date: Thu, 30 Jun 2011 21:34:00 +0000 Subject: [PATCH 044/494] Change how edit_area based editors fillout value. - Should fix failures due to edit_area. SVN r16610 |2011-06-30 21:34:00 +0000 --- src/org/labkey/test/bvt/SpecimenTest.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/org/labkey/test/bvt/SpecimenTest.java b/src/org/labkey/test/bvt/SpecimenTest.java index c36ed6e7..c9c4e1cb 100644 --- a/src/org/labkey/test/bvt/SpecimenTest.java +++ b/src/org/labkey/test/bvt/SpecimenTest.java @@ -86,9 +86,8 @@ protected void doTestSteps() selectQuery("study", "SpecimenDetail"); clickButton("Create New Query"); setFormElement("ff_newQueryName", REQUESTABILITY_QUERY); - clickLinkWithText("Create and Edit Source"); - toggleSQLQueryEditor(); - setFormElement("queryText", + clickLinkWithText("Create and Edit Source"); + setQueryEditorValue("queryText", "SELECT \n" + "SpecimenDetail.GlobalUniqueId AS GlobalUniqueId\n" + "FROM SpecimenDetail\n" + From 58fef4e7fe6ede83ea7ad63956fddc7577a1023d Mon Sep 17 00:00:00 2001 From: Trey Chadick Date: Thu, 21 Jul 2011 23:06:01 +0000 Subject: [PATCH 045/494] 11281: Validate queries more thoroughly in autmated tests SVN r16798 |2011-07-21 23:06:01 +0000 --- src/org/labkey/test/bvt/SpecimenTest.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/org/labkey/test/bvt/SpecimenTest.java b/src/org/labkey/test/bvt/SpecimenTest.java index c9c4e1cb..1d9d7fe0 100644 --- a/src/org/labkey/test/bvt/SpecimenTest.java +++ b/src/org/labkey/test/bvt/SpecimenTest.java @@ -48,6 +48,12 @@ public String getAssociatedModuleDirectory() return "server/modules/study"; } + @Override + protected String getProjectName() + { + return PROJECT_NAME; + } + @Override protected boolean isFileUploadTest() { From 71ea23aba92d2f94ebeae72a1557dd8c09744a53 Mon Sep 17 00:00:00 2001 From: Trey Chadick Date: Mon, 25 Jul 2011 22:19:49 +0000 Subject: [PATCH 046/494] 8766: Reorganize and rename tests Reorganize - merge bvt, daily, and drt namespaces. SVN r16825 |2011-07-25 22:19:49 +0000 --- src/org/labkey/test/{bvt => tests}/SpecimenTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename src/org/labkey/test/{bvt => tests}/SpecimenTest.java (97%) diff --git a/src/org/labkey/test/bvt/SpecimenTest.java b/src/org/labkey/test/tests/SpecimenTest.java similarity index 97% rename from src/org/labkey/test/bvt/SpecimenTest.java rename to src/org/labkey/test/tests/SpecimenTest.java index 1d9d7fe0..0c6a4887 100644 --- a/src/org/labkey/test/bvt/SpecimenTest.java +++ b/src/org/labkey/test/tests/SpecimenTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.labkey.test.bvt; +package org.labkey.test.tests; import org.labkey.test.BaseSeleniumWebTest; import org.labkey.test.Locator; From 678a3228a99c5e4164c182dd3fed81e81e0a9322 Mon Sep 17 00:00:00 2001 From: Trey Chadick Date: Mon, 8 Aug 2011 22:44:36 +0000 Subject: [PATCH 047/494] Test filtering specimen reports by participant group SVN r16997 |2011-08-08 22:44:36 +0000 --- src/org/labkey/test/tests/SpecimenTest.java | 37 ++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/src/org/labkey/test/tests/SpecimenTest.java b/src/org/labkey/test/tests/SpecimenTest.java index 0c6a4887..4d46c7a0 100644 --- a/src/org/labkey/test/tests/SpecimenTest.java +++ b/src/org/labkey/test/tests/SpecimenTest.java @@ -19,7 +19,6 @@ import org.labkey.test.BaseSeleniumWebTest; import org.labkey.test.Locator; import org.labkey.test.util.ExtHelper; -import org.labkey.test.util.PasswordUtil; import java.io.File; @@ -41,6 +40,7 @@ public class SpecimenTest extends BaseSeleniumWebTest private static final String USER2 = "user2@specimen.test"; private static final String REQUESTABILITY_QUERY = "RequestabilityRule"; private static final String UNREQUESTABLE_SAMPLE = "BAA07XNP-02"; + private static final String[] PTIDS = {"999320396","999320812"}; public String getAssociatedModuleDirectory() @@ -133,6 +133,16 @@ protected void doTestSteps() SpecimenImporter importer = new SpecimenImporter(new File(_studyDataRoot), new File(getLabKeyRoot(), SPECIMEN_ARCHIVE), new File(getLabKeyRoot(), SPECIMEN_TEMP_DIR), FOLDER_NAME, 1); importer.importAndWaitForComplete(); + clickLinkWithText("My Study"); + clickLinkWithText("Manage Study"); + clickLinkWithText("Manage Participant Groups"); + log("Set up participant groups"); + clickNavButton("Create", 0); + ExtHelper.waitForExtDialog(this, "Define Participant Group"); + setFormElement("categoryLabel", "Category1"); + setFormElement("categoryIdentifiers", PTIDS[0] + "," + PTIDS[1]); + ExtHelper.clickExtButton(this, "Define Participant Group", "Save", 0); + // Field check for Tube Type column (including conflict) clickLinkWithText(STUDY_NAME); clickLinkWithText("By Vial"); @@ -316,6 +326,31 @@ protected void doTestSteps() clickMenuButton("Request Options", "Create New Request"); clickNavButton("Cancel"); + log("check reports by participant group"); + clickLinkWithText("My Study"); + clickLinkWithText("Blood (Whole)"); + clickLinkWithText("Reports"); + clickNavButton("View"); // Summary Report + //Verify by vial count + assertElementPresent(Locator.xpath("//a[number(text()) > 0]"), 38); + selectOptionByText("participantGroupFilter", "Category1"); + clickNavButton("Refresh"); + assertElementNotPresent(Locator.xpath("//a[number(text()) > 6]")); + assertElementPresent(Locator.xpath("//a[number(text()) <= 6]"), 8); + selectOptionByText("participantGroupFilter", "All Groups"); + clickNavButton("Refresh"); + assertElementPresent(Locator.xpath("//a[number(text()) > 0]"), 38); + //Verify by ptid list + checkCheckbox("viewPtidList"); + uncheckCheckbox("viewVialCount"); + clickNavButton("Refresh"); + assertLinkPresentWithTextCount(PTIDS[0], 3); + assertLinkPresentWithTextCount(PTIDS[1], 5); + selectOptionByText("participantGroupFilter", "Category1"); + clickNavButton("Refresh"); + assertLinkPresentWithTextCount(PTIDS[0], 3); + assertLinkPresentWithTextCount(PTIDS[1], 5); + log("Check notification emails"); goToModule("Dumbster"); assertTextPresent("Specimen Request Notification", 8); From 622c01067eea25da7bb2f395f757e491eafc69bb Mon Sep 17 00:00:00 2001 From: Josh Eckels Date: Thu, 10 Nov 2011 20:57:07 +0000 Subject: [PATCH 048/494] Make CHAVI study folder type the default study folder type. Preserve existing study portal pages as the Overview tab. Fix up many, many tests. Hopefully all of them. Issue 13343: **Export buttons for peptide grid on protein detail page return run-level data Issue 13340: **duplicate link in nav trail and tabs in manage comments section Issue 13314: **Navigation - get "wrong" study home page when using nav trail Issue 13306: **Navigation - Import Study page has too many titles/links back to study Issue 13254: **Server throws exception when attempting to view study redesign or Letvin tab added as a custom tab, rather than a project type SVN r17947 |2011-11-10 20:57:07 +0000 --- src/org/labkey/test/tests/SpecimenTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/org/labkey/test/tests/SpecimenTest.java b/src/org/labkey/test/tests/SpecimenTest.java index 4d46c7a0..c636793c 100644 --- a/src/org/labkey/test/tests/SpecimenTest.java +++ b/src/org/labkey/test/tests/SpecimenTest.java @@ -145,6 +145,7 @@ protected void doTestSteps() // Field check for Tube Type column (including conflict) clickLinkWithText(STUDY_NAME); + addWebPart("Specimens"); clickLinkWithText("By Vial"); setFilter("SpecimenDetail", "PrimaryType", "Is Blank"); // Verify that there's only one vial of unknown type: @@ -208,7 +209,7 @@ protected void doTestSteps() selectOptionByText("generalActor", "SLG"); setFormElement("generalDescription", "SLG Approval"); clickLink(Locator.xpath("//input[@name='generalDescription']/../.." + Locator.navButton("Add Requirement").getPath())); - clickLinkWithText("Manage Study"); + clickTab("Manage"); clickLinkWithText("Manage New Request Form"); clickNavButton("Add New Input", 0); From ce00c07ba7637969f1fb38fe952a5d95d546f531 Mon Sep 17 00:00:00 2001 From: Britt Piehler Date: Thu, 8 Dec 2011 05:12:33 +0000 Subject: [PATCH 049/494] 13452: Study module - Missing specimen web parts SVN r18317 |2011-12-08 05:12:33 +0000 --- src/org/labkey/test/tests/SpecimenTest.java | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/org/labkey/test/tests/SpecimenTest.java b/src/org/labkey/test/tests/SpecimenTest.java index c636793c..2e85b25f 100644 --- a/src/org/labkey/test/tests/SpecimenTest.java +++ b/src/org/labkey/test/tests/SpecimenTest.java @@ -146,7 +146,7 @@ protected void doTestSteps() // Field check for Tube Type column (including conflict) clickLinkWithText(STUDY_NAME); addWebPart("Specimens"); - clickLinkWithText("By Vial"); + clickLinkWithText("By Individual Vial"); setFilter("SpecimenDetail", "PrimaryType", "Is Blank"); // Verify that there's only one vial of unknown type: assertLinkPresentWithTextCount("[history]", 1); @@ -158,6 +158,7 @@ protected void doTestSteps() assertTextPresent("20ml Cryovial"); assertTextPresent("25ml Cryovial"); clickLinkWithText("Specimen Overview"); + clickLinkWithText("Vials by Derivative", false); clickLinkWithText("Tear Flo Strips"); // For these three vials, there should be no conflict in TubeType, so we should see the text once for each of three vials: assertLinkPresentWithTextCount("[history]", 3); @@ -165,7 +166,8 @@ protected void doTestSteps() // specimen management setup clickLinkWithText(STUDY_NAME); - clickLinkWithText("Manage Statuses"); + clickLinkWithText("Manage Study"); + clickLinkWithText("Manage Request Statuses"); setFormElement("newLabel", "New Request"); clickNavButton("Save"); setFormElement("newLabel", "Processing"); @@ -196,6 +198,7 @@ protected void doTestSteps() clickLinkWithText("Update Members"); clickLinkWithText(STUDY_NAME); + clickLinkWithText("Manage Study"); clickLinkWithText("Manage Default Requirements"); selectOptionByText("originatorActor", "IRB"); setFormElement("originatorDescription", "Originating IRB Approval"); @@ -220,6 +223,8 @@ protected void doTestSteps() clickLinkWithText(STUDY_NAME); // create request + clickLinkWithText("Specimen Data"); + clickLinkWithText("Vials by Derivative", false); clickLinkWithText("Plasma, Unknown Processing"); // Verify unavailable sample assertElementPresent(Locator.xpath("//input[@id='check_" + UNREQUESTABLE_SAMPLE + "' and @disabled]")); @@ -253,6 +258,8 @@ protected void doTestSteps() clickLinkWithText(STUDY_NAME); // add additional specimens + clickLinkWithText("Specimen Data"); + clickLinkWithText("Vials by Derivative", false); clickLinkWithText("Swab"); checkCheckbox(".toggle"); clickMenuButtonAndContinue("Request Options", "Add To Existing Request"); @@ -322,6 +329,8 @@ protected void doTestSteps() assertTrue(getConfirmationAndWait().matches("^Canceling will permanently delete this pending request\\. Continue[\\s\\S]$")); assertTextPresent("No data to show."); clickLinkWithText(STUDY_NAME); + clickLinkWithText("Specimen Data"); + clickLinkWithText("Vials by Derivative", false); clickLinkWithText("Swab"); checkCheckbox(".toggle"); clickMenuButton("Request Options", "Create New Request"); @@ -329,6 +338,8 @@ protected void doTestSteps() log("check reports by participant group"); clickLinkWithText("My Study"); + clickLinkWithText("Specimen Data"); + clickLinkWithText("Vials by Primary Type", false); clickLinkWithText("Blood (Whole)"); clickLinkWithText("Reports"); clickNavButton("View"); // Summary Report From 708036bb40b95f8e285beff13deb3b07b705ed90 Mon Sep 17 00:00:00 2001 From: Trey Chadick Date: Wed, 25 Jan 2012 22:24:50 +0000 Subject: [PATCH 050/494] Fix for intermittent failures. SVN r18764 |2012-01-25 22:24:50 +0000 --- src/org/labkey/test/tests/SpecimenTest.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/org/labkey/test/tests/SpecimenTest.java b/src/org/labkey/test/tests/SpecimenTest.java index 2e85b25f..4ea9b44d 100644 --- a/src/org/labkey/test/tests/SpecimenTest.java +++ b/src/org/labkey/test/tests/SpecimenTest.java @@ -159,7 +159,7 @@ protected void doTestSteps() assertTextPresent("25ml Cryovial"); clickLinkWithText("Specimen Overview"); clickLinkWithText("Vials by Derivative", false); - clickLinkWithText("Tear Flo Strips"); + waitAndClick(WAIT_FOR_JAVASCRIPT, Locator.linkWithText("Tear Flo Strips"), WAIT_FOR_PAGE); // For these three vials, there should be no conflict in TubeType, so we should see the text once for each of three vials: assertLinkPresentWithTextCount("[history]", 3); assertTextPresent("15ml Cryovial", 3); @@ -225,7 +225,7 @@ protected void doTestSteps() // create request clickLinkWithText("Specimen Data"); clickLinkWithText("Vials by Derivative", false); - clickLinkWithText("Plasma, Unknown Processing"); + waitAndClick(WAIT_FOR_JAVASCRIPT, Locator.linkWithText("Plasma, Unknown Processing"), WAIT_FOR_PAGE); // Verify unavailable sample assertElementPresent(Locator.xpath("//input[@id='check_" + UNREQUESTABLE_SAMPLE + "' and @disabled]")); assertElementPresent(Locator.xpath("//input[@id='check_" + UNREQUESTABLE_SAMPLE + "']/../../td[contains(text(), 'This vial is unavailable because it was found in the set called \"" + REQUESTABILITY_QUERY + "\".')]")); @@ -260,7 +260,7 @@ protected void doTestSteps() // add additional specimens clickLinkWithText("Specimen Data"); clickLinkWithText("Vials by Derivative", false); - clickLinkWithText("Swab"); + waitAndClick(WAIT_FOR_JAVASCRIPT, Locator.linkWithText("Swab"), WAIT_FOR_PAGE); checkCheckbox(".toggle"); clickMenuButtonAndContinue("Request Options", "Add To Existing Request"); ExtHelper.waitForExtDialog(this, "Request Vial", WAIT_FOR_JAVASCRIPT); @@ -331,7 +331,7 @@ protected void doTestSteps() clickLinkWithText(STUDY_NAME); clickLinkWithText("Specimen Data"); clickLinkWithText("Vials by Derivative", false); - clickLinkWithText("Swab"); + waitAndClick(WAIT_FOR_JAVASCRIPT, Locator.linkWithText("Swab"), WAIT_FOR_PAGE); checkCheckbox(".toggle"); clickMenuButton("Request Options", "Create New Request"); clickNavButton("Cancel"); @@ -340,7 +340,7 @@ protected void doTestSteps() clickLinkWithText("My Study"); clickLinkWithText("Specimen Data"); clickLinkWithText("Vials by Primary Type", false); - clickLinkWithText("Blood (Whole)"); + waitAndClick(WAIT_FOR_JAVASCRIPT, Locator.linkWithText("Blood (Whole)"), WAIT_FOR_PAGE); clickLinkWithText("Reports"); clickNavButton("View"); // Summary Report //Verify by vial count From 4b090d9744dc9c711f474f67cbf400cae07256ca Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Thu, 26 Jan 2012 04:07:22 +0000 Subject: [PATCH 051/494] Update licenses SVN r18783 |2012-01-26 04:07:22 +0000 --- src/org/labkey/test/tests/SpecimenTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/org/labkey/test/tests/SpecimenTest.java b/src/org/labkey/test/tests/SpecimenTest.java index 4ea9b44d..eb12e6b4 100644 --- a/src/org/labkey/test/tests/SpecimenTest.java +++ b/src/org/labkey/test/tests/SpecimenTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007-2011 LabKey Corporation + * Copyright (c) 2007-2012 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. From ab40a33f442bfd15687af2a63f66f3100ce021b5 Mon Sep 17 00:00:00 2001 From: Karl Lum Date: Wed, 22 Feb 2012 01:48:31 +0000 Subject: [PATCH 052/494] 14087: Specimen report aggregates can be incorrect for zero date values (fix from britt) SVN r19087 |2012-02-22 01:48:31 +0000 --- src/org/labkey/test/tests/SpecimenTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/org/labkey/test/tests/SpecimenTest.java b/src/org/labkey/test/tests/SpecimenTest.java index eb12e6b4..6d2c2d9b 100644 --- a/src/org/labkey/test/tests/SpecimenTest.java +++ b/src/org/labkey/test/tests/SpecimenTest.java @@ -344,14 +344,14 @@ protected void doTestSteps() clickLinkWithText("Reports"); clickNavButton("View"); // Summary Report //Verify by vial count - assertElementPresent(Locator.xpath("//a[number(text()) > 0]"), 38); + assertElementPresent(Locator.xpath("//a[number(text()) > 0]"), 36); selectOptionByText("participantGroupFilter", "Category1"); clickNavButton("Refresh"); assertElementNotPresent(Locator.xpath("//a[number(text()) > 6]")); assertElementPresent(Locator.xpath("//a[number(text()) <= 6]"), 8); selectOptionByText("participantGroupFilter", "All Groups"); clickNavButton("Refresh"); - assertElementPresent(Locator.xpath("//a[number(text()) > 0]"), 38); + assertElementPresent(Locator.xpath("//a[number(text()) > 0]"), 36); //Verify by ptid list checkCheckbox("viewPtidList"); uncheckCheckbox("viewVialCount"); From 89aa24b27d49b416ef55b7bd2b1d3f44cccdedc8 Mon Sep 17 00:00:00 2001 From: Elizabeth Vannostrand Date: Wed, 20 Jun 2012 19:05:01 +0000 Subject: [PATCH 053/494] Add tests to verify that lists, specimens, and datasets all log to audit trail when exporting data SVN r20812 |2012-06-20 19:05:01 +0000 --- src/org/labkey/test/tests/SpecimenTest.java | 40 ++++++++++++++++++--- 1 file changed, 35 insertions(+), 5 deletions(-) diff --git a/src/org/labkey/test/tests/SpecimenTest.java b/src/org/labkey/test/tests/SpecimenTest.java index 6d2c2d9b..6350a94c 100644 --- a/src/org/labkey/test/tests/SpecimenTest.java +++ b/src/org/labkey/test/tests/SpecimenTest.java @@ -18,7 +18,9 @@ import org.labkey.test.BaseSeleniumWebTest; import org.labkey.test.Locator; +import org.labkey.test.util.DataRegionTable; import org.labkey.test.util.ExtHelper; +import org.labkey.test.util.PasswordUtil; import java.io.File; @@ -33,6 +35,7 @@ public class SpecimenTest extends BaseSeleniumWebTest protected static final String FOLDER_NAME = "My Study"; private static final String SPECIMEN_ARCHIVE = "/sampledata/study/specimens/sample_a.specimens"; private static final String SPECIMEN_TEMP_DIR = "/sampledata/study/drt_temp"; + public static final String SPECIMEN_DETAIL = "SpecimenDetail"; private String _studyDataRoot = null; private static final String STUDY_NAME = "My Study Study"; private static final String DESTINATION_SITE = "Aurum Health KOSH Lab, Orkney, South Africa (Repository)"; @@ -89,15 +92,15 @@ protected void doTestSteps() // Create custom query to test requestability rules. goToSchemaBrowser(); - selectQuery("study", "SpecimenDetail"); + selectQuery("study", SPECIMEN_DETAIL); clickButton("Create New Query"); setFormElement("ff_newQueryName", REQUESTABILITY_QUERY); clickLinkWithText("Create and Edit Source"); setQueryEditorValue("queryText", "SELECT \n" + - "SpecimenDetail.GlobalUniqueId AS GlobalUniqueId\n" + - "FROM SpecimenDetail\n" + - "WHERE SpecimenDetail.GlobalUniqueId='" + UNREQUESTABLE_SAMPLE + "'"); + SPECIMEN_DETAIL + ".GlobalUniqueId AS GlobalUniqueId\n" + + "FROM " + SPECIMEN_DETAIL + "\n" + + "WHERE " + SPECIMEN_DETAIL + ".GlobalUniqueId='" + UNREQUESTABLE_SAMPLE + "'"); clickButton("Save", 0); waitForText("Saved", WAIT_FOR_JAVASCRIPT); @@ -147,7 +150,7 @@ protected void doTestSteps() clickLinkWithText(STUDY_NAME); addWebPart("Specimens"); clickLinkWithText("By Individual Vial"); - setFilter("SpecimenDetail", "PrimaryType", "Is Blank"); + setFilter(SPECIMEN_DETAIL, "PrimaryType", "Is Blank"); // Verify that there's only one vial of unknown type: assertLinkPresentWithTextCount("[history]", 1); // There's a conflict in TubeType for this vial's events; verify that no TubeType is populated at the vial level @@ -341,6 +344,7 @@ protected void doTestSteps() clickLinkWithText("Specimen Data"); clickLinkWithText("Vials by Primary Type", false); waitAndClick(WAIT_FOR_JAVASCRIPT, Locator.linkWithText("Blood (Whole)"), WAIT_FOR_PAGE); + pushLocation(); clickLinkWithText("Reports"); clickNavButton("View"); // Summary Report //Verify by vial count @@ -386,5 +390,31 @@ protected void doTestSteps() clickLinkContainingText("Specimen Request Notification", 1, false); assertTextPresent(specimen1); } + + exportSpecimenTest(); + } + + private void exportSpecimenTest() + { + popLocation(); + addUrlParameter("&exportType=excelWebQuery"); + assertTextPresent("org.labkey.study.query.SpecimenRequestDisplayColumn"); + goBack(); + + + goToAuditLog(); + selectOptionByText("view", "Query events"); + waitForPageToLoad(); + + DataRegionTable auditTable = new DataRegionTable("audit", this); + String[][] columnAndValues = new String[][] {{"Created By", PasswordUtil.getUsername()}, + {"Project", PROJECT_NAME}, {"Container", FOLDER_NAME}, {"SchemaName", "study"}, + {"QueryName", SPECIMEN_DETAIL}, {"Comment", "Exported to Excel Web Query data"}}; + for(String[] columnAndValue : columnAndValues) + { + log("Checking column: "+ columnAndValue[0]); + assertEquals(columnAndValue[1], auditTable.getDataAsText(0, columnAndValue[0])); + } + //To change body of created methods use File | Settings | File Templates. } } From db98f991b2b34a576b9f83e8fdc8a8da369b096a Mon Sep 17 00:00:00 2001 From: Elizabeth Vannostrand Date: Tue, 26 Jun 2012 15:57:58 +0000 Subject: [PATCH 054/494] update specimen test to work with new search SVN r20893 |2012-06-26 15:57:58 +0000 --- src/org/labkey/test/tests/SpecimenTest.java | 36 +++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/org/labkey/test/tests/SpecimenTest.java b/src/org/labkey/test/tests/SpecimenTest.java index 6350a94c..6761705d 100644 --- a/src/org/labkey/test/tests/SpecimenTest.java +++ b/src/org/labkey/test/tests/SpecimenTest.java @@ -18,11 +18,16 @@ import org.labkey.test.BaseSeleniumWebTest; import org.labkey.test.Locator; +import org.labkey.test.util.ComponentQuery; import org.labkey.test.util.DataRegionTable; +import org.labkey.test.util.Ext4Helper; import org.labkey.test.util.ExtHelper; import org.labkey.test.util.PasswordUtil; +import org.labkey.test.util.ext4cmp.Ext4CmpRef; +import org.labkey.test.util.ext4cmp.Ext4FieldRef; import java.io.File; +import java.util.List; /** * User: brittp @@ -392,6 +397,37 @@ protected void doTestSteps() } exportSpecimenTest(); + searchTest(); + } + + private void searchTest() + { + goToProjectHome(); + clickLinkContainingText(FOLDER_NAME); + clickTab("Specimen Data"); + waitForPageToLoad(); + waitForExt4(); + Ext4FieldRef additiveType = Ext4FieldRef.getForLabel(this, "Additive Type"); + additiveType.setValue("Heparin"); + Ext4FieldRef.getForLabel(this, "Participant").setValue("999320812"); + clickButtonContainingText("Search"); + assertTextNotPresent("Serum Separator"); + assertTextPresent("(ParticipantId = 999320812) AND (AdditiveType = Heparin)"); + goBack(); + waitForExt4(); + additiveType.setValue(new String[] {"Heparin", "Ammounium Heparin"}); + clickButtonContainingText("Search"); + assertTextPresent("ONE OF"); + goBack(); + waitForExt4(); + additiveType.setValue(new String[] {"Ammonium Heparin","Cell Preparation Tube Heparin","Cell Preparation Tube SCI","Citrate Phosphate Dextrose","EDTA","Fetal Fibronectin Buffer","Guanidine Isothiocyanate (GITC)","Heparin","Liquid Potassium EDTA","Liquid Sodium EDTA","Lithium Heparin","Lithium Heparin and Gel for Plasma","None","Normal Saline","Optimum Cutting Temperature Medium","Orasure Collection Container","Other","PAXgene Blood RNA tube","Phosphate Buffered Saline","Plasma Preparation Tube","PLP Fixative","Port-a-cul Transport Tube","Potassium EDTA","RNA Later","Serum Separator","Sodium Citrate","Sodium EDTA","Sodium Fluoride","Sodium Fluoride/Potassium Oxalate","Sodium Heparin","Sodium Polyanetholesulfonate","Spray Dried Potassium EDTA","Spray Dried Sodium EDTA","Thrombin","Tissue Freezing Medium","Unknown Additive","Viral Transport Media"}); + clickButtonContainingText("Search"); + assertTextPresent("IS NOT ANY OF "); + } + + private void waitForExt4() + { + sleep(2000); //TODO: this needs something better } private void exportSpecimenTest() From 004eff75b38da131db381261bf4865c460a0be7a Mon Sep 17 00:00:00 2001 From: Dave Bradlee Date: Wed, 27 Jun 2012 18:35:35 +0000 Subject: [PATCH 055/494] Story: Upload barcode and manage notifications -- Added test for these to SpecimenTest. SVN r20934 |2012-06-27 18:35:35 +0000 --- src/org/labkey/test/tests/SpecimenTest.java | 55 +++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/src/org/labkey/test/tests/SpecimenTest.java b/src/org/labkey/test/tests/SpecimenTest.java index 6761705d..6553be72 100644 --- a/src/org/labkey/test/tests/SpecimenTest.java +++ b/src/org/labkey/test/tests/SpecimenTest.java @@ -230,7 +230,62 @@ protected void doTestSteps() clickNavButton("Save"); clickLinkWithText(STUDY_NAME); + // Test "Configure Defaults for Actor Notification" + log("Check Configure Defaults for Actor Notification"); + clickLinkWithText(STUDY_NAME); + clickLinkWithText("Manage"); + clickLinkWithText("Manage Notifications"); + assertTextPresent("Default Email Recipients"); + checkRadioButton("defaultEmailNotify", "All"); + clickNavButton("Save"); + + // Test Upload Specimen List from file/paste + log("Check Upload Specimen List from file"); + clickLinkWithText(STUDY_NAME); + clickLinkWithText("Specimen Data"); + clickLinkWithText("Create New Request"); + selectOptionByText("destinationSite", "Aurum Health KOSH Lab, Orkney, South Africa (Repository)"); + setFormElement("input0", "Assay Plan"); + setFormElement("input2", "Comments"); + setFormElement("input1", "Shipping"); + setFormElement("input3", "Last one"); + clickNavButton("Create and View Details"); + clickLinkWithText("Upload Specimen Ids"); + setFormElement(Locator.xpath("//textarea[@id='tsv3']"), "AAA07XK5-01"); // add specimen + clickNavButton("Submit"); // Submit button + + clickLinkWithText("Upload Specimen Ids"); + setFormElement(Locator.xpath("//textarea[@id='tsv3']"), "AAA07XK5-01"); // try to add again + clickNavButton("Submit", 0); // Submit button + waitForText("Specimen AAA07XK5-01 not available", 20000); + setFormElement(Locator.xpath("//textarea[@id='tsv3']"), "AAA07XK5-02"); // try to add one that doesn't exist + clickNavButton("Submit", 0); // Submit button + waitForText("Specimen AAA07XK5-02 not available", 20000); + setFormElement(Locator.xpath("//textarea[@id='tsv3']"), "AAA07XK5-04\nAAA07XK5-06\nAAA07XSF-03"); // add different one + clickNavButton("Submit"); // Submit button + + // Check each Actor's Details for "Default Actor Notification" feature; + // In Details, for each actor the ether Notify checkbox should be set or disabled, because we set Notifications to All + List detailsLinks = findAllMatches(Locator.xpath("//td[a='Details']/a")); + for (Locator link : detailsLinks) + { + clickLink(link); + List allCheckBoxes = findAllMatches(Locator.xpath("//input[@type='checkbox' and @name='notificationIdPairs']")); + List checkedCheckBoxes = findAllMatches(Locator.xpath("//input[@type='checkbox' and @name='notificationIdPairs' and @checked]")); + List disabledCheckBoxes = findAllMatches(Locator.xpath("//input[@type='checkbox' and @name='notificationIdPairs' and @disabled]")); + assertTrue("Actor Notification: All actors should be notified if addresses configured.", allCheckBoxes.size() == checkedCheckBoxes.size() + disabledCheckBoxes.size()); + clickNavButton("Cancel"); + } + + assertTextPresent("Associated Specimens"); + assertTextPresent("AAA07XK5-01", "AAA07XK5-04", "AAA07XK5-06", "AAA07XSF-03"); + + + clickNavButton("Cancel Request"); + assertTrue(getConfirmationAndWait().matches("^Canceling will permanently delete this pending request\\. Continue[\\s\\S]$")); + // create request + clickLinkWithText(STUDY_NAME); clickLinkWithText("Specimen Data"); clickLinkWithText("Vials by Derivative", false); waitAndClick(WAIT_FOR_JAVASCRIPT, Locator.linkWithText("Plasma, Unknown Processing"), WAIT_FOR_PAGE); From 5ebe19c4c1fc36982cbd01ed9d0b10e887a705cf Mon Sep 17 00:00:00 2001 From: Alan Vezina Date: Wed, 11 Jul 2012 01:36:06 +0000 Subject: [PATCH 056/494] Issue 15229: Generic Chart UI should resemble the Time Chart UI - Changed layout to card layout. - Options panel hidden until button pressed. - Added view data / view chart button. - Added options panel button. - Added apply button to options panel. - Panels now styled similarly to time chart/ dataviews Fixed SpecimenTest failure. SVN r21103 |2012-07-11 01:36:06 +0000 --- src/org/labkey/test/tests/SpecimenTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/org/labkey/test/tests/SpecimenTest.java b/src/org/labkey/test/tests/SpecimenTest.java index 6553be72..85d39365 100644 --- a/src/org/labkey/test/tests/SpecimenTest.java +++ b/src/org/labkey/test/tests/SpecimenTest.java @@ -147,7 +147,7 @@ protected void doTestSteps() log("Set up participant groups"); clickNavButton("Create", 0); ExtHelper.waitForExtDialog(this, "Define Participant Group"); - setFormElement("categoryLabel", "Category1"); + setFormElement("groupLabel", "Category1"); setFormElement("categoryIdentifiers", PTIDS[0] + "," + PTIDS[1]); ExtHelper.clickExtButton(this, "Define Participant Group", "Save", 0); From 766fcf44603c80fa6cadb64d6e8d42abda4dc2c8 Mon Sep 17 00:00:00 2001 From: Kevin Krouse Date: Mon, 16 Jul 2012 03:45:46 +0000 Subject: [PATCH 057/494] Issue 15493: InvalidKeyException in org.labkey.study.query.CohortUpdateService.keyFromMap() SVN r21170 |2012-07-16 03:45:46 +0000 --- src/org/labkey/test/tests/SpecimenTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/org/labkey/test/tests/SpecimenTest.java b/src/org/labkey/test/tests/SpecimenTest.java index 85d39365..53995c2a 100644 --- a/src/org/labkey/test/tests/SpecimenTest.java +++ b/src/org/labkey/test/tests/SpecimenTest.java @@ -498,7 +498,7 @@ private void exportSpecimenTest() waitForPageToLoad(); DataRegionTable auditTable = new DataRegionTable("audit", this); - String[][] columnAndValues = new String[][] {{"Created By", PasswordUtil.getUsername()}, + String[][] columnAndValues = new String[][] {{"Created By", getDisplayName()}, {"Project", PROJECT_NAME}, {"Container", FOLDER_NAME}, {"SchemaName", "study"}, {"QueryName", SPECIMEN_DETAIL}, {"Comment", "Exported to Excel Web Query data"}}; for(String[] columnAndValue : columnAndValues) From c05c7e6faca803f231364af83e7b17e00b92cb44 Mon Sep 17 00:00:00 2001 From: Josh Eckels Date: Mon, 23 Jul 2012 22:43:19 +0000 Subject: [PATCH 058/494] Use client API (via Java binding) to create projects by default in Selenium tests. Saves approximately 7 seconds per project creation. Introduce AbstractContainerHelper, with two implementations: APIContainerHelper and UIContainerHelper. BaseSeleniumWebTest defaults to using APIContainerHelper, but BasicTest and MS1Test specifically use UIContainerHelper instead to maintain test coverage of HTML UI. In the future, we can create subfolders via the API as well to further improve test performance. SVN r21314 |2012-07-23 22:43:19 +0000 --- src/org/labkey/test/tests/SpecimenTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/org/labkey/test/tests/SpecimenTest.java b/src/org/labkey/test/tests/SpecimenTest.java index 53995c2a..fc02ffde 100644 --- a/src/org/labkey/test/tests/SpecimenTest.java +++ b/src/org/labkey/test/tests/SpecimenTest.java @@ -86,7 +86,7 @@ protected void doTestSteps() { _studyDataRoot = getLabKeyRoot() + "/sampledata/study"; - createProject(PROJECT_NAME); + _containerHelper.createProject(PROJECT_NAME, null); enableEmailRecorder(); From a3156c450726f2aa5e7d57e86af3a9355fe73f15 Mon Sep 17 00:00:00 2001 From: Trey Chadick Date: Tue, 31 Jul 2012 00:13:22 +0000 Subject: [PATCH 059/494] Upgrade selenium tests to use JUnit4 style test definition. Tests defined by @Test annotation rather than methods starting with 'test' SVN r21459 |2012-07-31 00:13:22 +0000 --- src/org/labkey/test/tests/SpecimenTest.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/org/labkey/test/tests/SpecimenTest.java b/src/org/labkey/test/tests/SpecimenTest.java index fc02ffde..cd882f62 100644 --- a/src/org/labkey/test/tests/SpecimenTest.java +++ b/src/org/labkey/test/tests/SpecimenTest.java @@ -16,6 +16,7 @@ package org.labkey.test.tests; +import org.junit.Assert; import org.labkey.test.BaseSeleniumWebTest; import org.labkey.test.Locator; import org.labkey.test.util.ComponentQuery; @@ -273,7 +274,7 @@ protected void doTestSteps() List allCheckBoxes = findAllMatches(Locator.xpath("//input[@type='checkbox' and @name='notificationIdPairs']")); List checkedCheckBoxes = findAllMatches(Locator.xpath("//input[@type='checkbox' and @name='notificationIdPairs' and @checked]")); List disabledCheckBoxes = findAllMatches(Locator.xpath("//input[@type='checkbox' and @name='notificationIdPairs' and @disabled]")); - assertTrue("Actor Notification: All actors should be notified if addresses configured.", allCheckBoxes.size() == checkedCheckBoxes.size() + disabledCheckBoxes.size()); + Assert.assertTrue("Actor Notification: All actors should be notified if addresses configured.", allCheckBoxes.size() == checkedCheckBoxes.size() + disabledCheckBoxes.size()); clickNavButton("Cancel"); } @@ -282,7 +283,7 @@ protected void doTestSteps() clickNavButton("Cancel Request"); - assertTrue(getConfirmationAndWait().matches("^Canceling will permanently delete this pending request\\. Continue[\\s\\S]$")); + Assert.assertTrue(getConfirmationAndWait().matches("^Canceling will permanently delete this pending request\\. Continue[\\s\\S]$")); // create request clickLinkWithText(STUDY_NAME); @@ -350,7 +351,7 @@ protected void doTestSteps() assertTextPresent("Not Yet Submitted"); assertTextNotPresent("New Request"); clickNavButton("Submit Request", 0); - assertTrue(getConfirmationAndWait().matches("^Once a request is submitted, its specimen list may no longer be modified\\. Continue[\\s\\S]$")); + Assert.assertTrue(getConfirmationAndWait().matches("^Once a request is submitted, its specimen list may no longer be modified\\. Continue[\\s\\S]$")); assertTextNotPresent("Not Yet Submitted"); assertTextPresent("New Request"); @@ -389,7 +390,7 @@ protected void doTestSteps() selectOptionByText("status", "Not Yet Submitted"); clickNavButton("Save Changes and Send Notifications"); clickNavButton("Cancel Request", 0); - assertTrue(getConfirmationAndWait().matches("^Canceling will permanently delete this pending request\\. Continue[\\s\\S]$")); + Assert.assertTrue(getConfirmationAndWait().matches("^Canceling will permanently delete this pending request\\. Continue[\\s\\S]$")); assertTextPresent("No data to show."); clickLinkWithText(STUDY_NAME); clickLinkWithText("Specimen Data"); @@ -504,7 +505,7 @@ private void exportSpecimenTest() for(String[] columnAndValue : columnAndValues) { log("Checking column: "+ columnAndValue[0]); - assertEquals(columnAndValue[1], auditTable.getDataAsText(0, columnAndValue[0])); + Assert.assertEquals(columnAndValue[1], auditTable.getDataAsText(0, columnAndValue[0])); } //To change body of created methods use File | Settings | File Templates. } From b87204c12fe470463eea2f8ea7dcedac5baa6f11 Mon Sep 17 00:00:00 2001 From: Elizabeth Vannostrand Date: Thu, 9 Aug 2012 21:47:17 +0000 Subject: [PATCH 060/494] convert more tests to use subfolder creation api rather than ui SVN r21569 |2012-08-09 21:47:17 +0000 --- src/org/labkey/test/tests/SpecimenTest.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/org/labkey/test/tests/SpecimenTest.java b/src/org/labkey/test/tests/SpecimenTest.java index cd882f62..a6725081 100644 --- a/src/org/labkey/test/tests/SpecimenTest.java +++ b/src/org/labkey/test/tests/SpecimenTest.java @@ -295,6 +295,12 @@ protected void doTestSteps() assertElementPresent(Locator.xpath("//input[@id='check_" + UNREQUESTABLE_SAMPLE + "']/../../td[contains(text(), 'This vial is unavailable because it was found in the set called \"" + REQUESTABILITY_QUERY + "\".')]")); assertElementPresent(Locator.xpath("//input[@id='check_" + UNREQUESTABLE_SAMPLE + "']/../a[contains(@onmouseover, 'This vial is unavailable because it was found in the set called \\\"" + REQUESTABILITY_QUERY + "\\\".')]")); checkCheckbox(".toggle"); + + clickMenuButton("Page Size", "Show All"); + clickLinkContainingText("history"); + assertTextPresent("Vial History"); + goBack(); + clickMenuButton("Request Options", "Create New Request"); selectOptionByText("destinationSite", "Aurum Health KOSH Lab, Orkney, South Africa (Repository)"); setFormElement("input0", "Assay Plan"); From 37c6c5ff2c805c06d7ff198bea9e58bde2f12ff0 Mon Sep 17 00:00:00 2001 From: Karl Lum Date: Wed, 19 Sep 2012 20:54:46 +0000 Subject: [PATCH 061/494] Merge itn12.2 branch changes through r22103 (which includes release 12.2 changes through r22099) SVN r22106 |2012-09-19 20:54:46 +0000 --- src/org/labkey/test/tests/SpecimenTest.java | 283 +++++++++++++------- 1 file changed, 186 insertions(+), 97 deletions(-) diff --git a/src/org/labkey/test/tests/SpecimenTest.java b/src/org/labkey/test/tests/SpecimenTest.java index a6725081..f279311c 100644 --- a/src/org/labkey/test/tests/SpecimenTest.java +++ b/src/org/labkey/test/tests/SpecimenTest.java @@ -18,16 +18,19 @@ import org.junit.Assert; import org.labkey.test.BaseSeleniumWebTest; +import org.apache.http.HttpException; +import org.apache.http.HttpStatus; +import org.apache.james.mime4j.field.datetime.DateTime; import org.labkey.test.Locator; -import org.labkey.test.util.ComponentQuery; +import org.labkey.test.WebTestHelper; import org.labkey.test.util.DataRegionTable; -import org.labkey.test.util.Ext4Helper; import org.labkey.test.util.ExtHelper; import org.labkey.test.util.PasswordUtil; -import org.labkey.test.util.ext4cmp.Ext4CmpRef; import org.labkey.test.util.ext4cmp.Ext4FieldRef; -import java.io.File; +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.Date; import java.util.List; /** @@ -35,21 +38,17 @@ * Date: Mar 9, 2006 * Time: 1:54:57 PM */ -public class SpecimenTest extends BaseSeleniumWebTest +public class SpecimenTest extends StudyBaseTest { protected static final String PROJECT_NAME = "SpecimenVerifyProject"; - protected static final String FOLDER_NAME = "My Study"; - private static final String SPECIMEN_ARCHIVE = "/sampledata/study/specimens/sample_a.specimens"; - private static final String SPECIMEN_TEMP_DIR = "/sampledata/study/drt_temp"; public static final String SPECIMEN_DETAIL = "SpecimenDetail"; - private String _studyDataRoot = null; - private static final String STUDY_NAME = "My Study Study"; private static final String DESTINATION_SITE = "Aurum Health KOSH Lab, Orkney, South Africa (Repository)"; private static final String USER1 = "user1@specimen.test"; private static final String USER2 = "user2@specimen.test"; private static final String REQUESTABILITY_QUERY = "RequestabilityRule"; private static final String UNREQUESTABLE_SAMPLE = "BAA07XNP-02"; private static final String[] PTIDS = {"999320396","999320812"}; + private int _requestId; public String getAssociatedModuleDirectory() @@ -69,33 +68,34 @@ protected boolean isFileUploadTest() return true; } - protected void doCleanup() throws Exception - { - _studyDataRoot = getLabKeyRoot() + "/sampledata/study"; - File tempDir = new File(getLabKeyRoot() + SPECIMEN_TEMP_DIR); - if (tempDir.exists()) - { - for (File file : tempDir.listFiles()) - file.delete(); - tempDir.delete(); - } - try { deleteProject(PROJECT_NAME); } catch (Throwable e) {} - } - - - protected void doTestSteps() + @Override + protected void doCreateSteps() { - _studyDataRoot = getLabKeyRoot() + "/sampledata/study"; - - _containerHelper.createProject(PROJECT_NAME, null); - enableEmailRecorder(); + initializeFolder(); - createSubfolder(PROJECT_NAME, PROJECT_NAME, FOLDER_NAME, "Study", null); clickNavButton("Create Study"); + setFormElement(Locator.name("label"), getStudyLabel()); click(Locator.radioButtonByNameAndValue("simpleRepository", "false")); clickNavButton("Create Study"); + setPipelineRoot(getPipelinePath()); + + setupRequestabilityRules(); + startSpecimenImport(1); + waitForSpecimenImport(); + createParticipantGroups(); + checkTubeType(); + setupSpecimenManagement(); + setupActorsAndGroups(); + setupDefaultRequirements(); + setupRequestForm(); + setupActorNotification(); + uploadSpecimensFromFile(); + } + + private void setupRequestabilityRules() + { // Create custom query to test requestability rules. goToSchemaBrowser(); selectQuery("study", SPECIMEN_DETAIL); @@ -110,7 +110,7 @@ protected void doTestSteps() clickButton("Save", 0); waitForText("Saved", WAIT_FOR_JAVASCRIPT); - clickLinkWithText(STUDY_NAME); + clickLinkWithText(getStudyLabel()); clickLinkWithText("Manage Study"); clickLinkWithText("Manage Requestability Rules"); // Verify that LOCKED_IN_REQUEST is the last rule @@ -131,18 +131,11 @@ protected void doTestSteps() ExtHelper.selectComboBoxItem(this, Locator.xpath("//div[@id='x-form-el-userQuery_action']"), "Unavailable" ); clickButton("Submit",0); clickButton("Save"); - - - clickLinkWithText("My Study"); - - setPipelineRoot(_studyDataRoot); - clickLinkWithText("My Study"); - clickLinkWithText("Manage Files"); - - SpecimenImporter importer = new SpecimenImporter(new File(_studyDataRoot), new File(getLabKeyRoot(), SPECIMEN_ARCHIVE), new File(getLabKeyRoot(), SPECIMEN_TEMP_DIR), FOLDER_NAME, 1); - importer.importAndWaitForComplete(); + } - clickLinkWithText("My Study"); + private void createParticipantGroups() + { + clickLinkWithText(getFolderName()); clickLinkWithText("Manage Study"); clickLinkWithText("Manage Participant Groups"); log("Set up participant groups"); @@ -151,9 +144,12 @@ protected void doTestSteps() setFormElement("groupLabel", "Category1"); setFormElement("categoryIdentifiers", PTIDS[0] + "," + PTIDS[1]); ExtHelper.clickExtButton(this, "Define Participant Group", "Save", 0); + } + private void checkTubeType() + { // Field check for Tube Type column (including conflict) - clickLinkWithText(STUDY_NAME); + clickLinkWithText(getStudyLabel()); addWebPart("Specimens"); clickLinkWithText("By Individual Vial"); setFilter(SPECIMEN_DETAIL, "PrimaryType", "Is Blank"); @@ -172,9 +168,11 @@ protected void doTestSteps() // For these three vials, there should be no conflict in TubeType, so we should see the text once for each of three vials: assertLinkPresentWithTextCount("[history]", 3); assertTextPresent("15ml Cryovial", 3); + } - // specimen management setup - clickLinkWithText(STUDY_NAME); + private void setupSpecimenManagement() + { + clickLinkWithText(getStudyLabel()); clickLinkWithText("Manage Study"); clickLinkWithText("Manage Request Statuses"); setFormElement("newLabel", "New Request"); @@ -188,7 +186,10 @@ protected void doTestSteps() checkCheckbox("newFinalState"); uncheckCheckbox("newSpecimensLocked"); clickNavButton("Done"); + } + private void setupActorsAndGroups() + { clickLinkWithText("Manage Actors and Groups"); setFormElement("newLabel", "SLG"); selectOptionByText("newPerSite", "One Per Study"); @@ -205,8 +206,11 @@ protected void doTestSteps() setText("names", USER2); uncheckCheckbox("sendEmail"); clickLinkWithText("Update Members"); - clickLinkWithText(STUDY_NAME); + clickLinkWithText(getStudyLabel()); + } + private void setupDefaultRequirements() + { clickLinkWithText("Manage Study"); clickLinkWithText("Manage Default Requirements"); selectOptionByText("originatorActor", "IRB"); @@ -222,27 +226,34 @@ protected void doTestSteps() setFormElement("generalDescription", "SLG Approval"); clickLink(Locator.xpath("//input[@name='generalDescription']/../.." + Locator.navButton("Add Requirement").getPath())); clickTab("Manage"); - + } + + private void setupRequestForm() + { clickLinkWithText("Manage New Request Form"); clickNavButton("Add New Input", 0); setFormElement("//descendant::input[@name='title'][4]", "Last One"); setFormElement("//descendant::input[@name='helpText'][4]", "A test input"); click(Locator.xpath("//descendant::input[@name='required'][4]")); clickNavButton("Save"); - clickLinkWithText(STUDY_NAME); + clickLinkWithText(getStudyLabel()); + } - // Test "Configure Defaults for Actor Notification" + private void setupActorNotification() + { log("Check Configure Defaults for Actor Notification"); - clickLinkWithText(STUDY_NAME); + clickLinkWithText(getStudyLabel()); clickLinkWithText("Manage"); clickLinkWithText("Manage Notifications"); assertTextPresent("Default Email Recipients"); checkRadioButton("defaultEmailNotify", "All"); clickNavButton("Save"); + } - // Test Upload Specimen List from file/paste + private void uploadSpecimensFromFile() + { log("Check Upload Specimen List from file"); - clickLinkWithText(STUDY_NAME); + clickLinkWithText(getStudyLabel()); clickLinkWithText("Specimen Data"); clickLinkWithText("Create New Request"); selectOptionByText("destinationSite", "Aurum Health KOSH Lab, Orkney, South Africa (Repository)"); @@ -264,7 +275,23 @@ protected void doTestSteps() waitForText("Specimen AAA07XK5-02 not available", 20000); setFormElement(Locator.xpath("//textarea[@id='tsv3']"), "AAA07XK5-04\nAAA07XK5-06\nAAA07XSF-03"); // add different one clickNavButton("Submit"); // Submit button + } + + @Override + protected void doVerifySteps() + { + verifyActorDetails(); + createRequest(); + verifyViews(); + verifyNotificationEmails(); + verifyRequestCancel(); + verifyReports(); + exportSpecimenTest(); + searchTest(); + } + private void verifyActorDetails() + { // Check each Actor's Details for "Default Actor Notification" feature; // In Details, for each actor the ether Notify checkbox should be set or disabled, because we set Notifications to All List detailsLinks = findAllMatches(Locator.xpath("//td[a='Details']/a")); @@ -284,9 +311,11 @@ protected void doTestSteps() clickNavButton("Cancel Request"); Assert.assertTrue(getConfirmationAndWait().matches("^Canceling will permanently delete this pending request\\. Continue[\\s\\S]$")); + } - // create request - clickLinkWithText(STUDY_NAME); + private void createRequest() + { + clickLinkWithText(getStudyLabel()); clickLinkWithText("Specimen Data"); clickLinkWithText("Vials by Derivative", false); waitAndClick(WAIT_FOR_JAVASCRIPT, Locator.linkWithText("Plasma, Unknown Processing"), WAIT_FOR_PAGE); @@ -325,7 +354,7 @@ protected void doTestSteps() // verify that the swab specimen isn't present yet assertTextNotPresent("DAA07YGW-01"); assertTextNotPresent("Complete"); - clickLinkWithText(STUDY_NAME); + clickLinkWithText(getStudyLabel()); // add additional specimens clickLinkWithText("Specimen Data"); @@ -334,7 +363,7 @@ protected void doTestSteps() checkCheckbox(".toggle"); clickMenuButtonAndContinue("Request Options", "Add To Existing Request"); ExtHelper.waitForExtDialog(this, "Request Vial", WAIT_FOR_JAVASCRIPT); - sleep(10000); //TODO: Determine which specific element to wait for. + waitForElement(Locator.css("#request-vial-details .x-grid3-row")); clickNavButton("Add 8 Vials to Request", 0); ExtHelper.waitForExtDialog(this, "Success", WAIT_FOR_JAVASCRIPT * 5); clickNavButton("OK", 0); @@ -371,8 +400,10 @@ protected void doTestSteps() checkCheckbox("notificationIdPairs", 1); clickNavButton("Save Changes and Send Notifications"); assertTextPresent("Complete"); + } - // verify views + private void verifyViews() + { clickLinkWithText("View History"); assertTextPresent("Request submitted for processing."); assertTextPresent("Notification Sent", 2); @@ -382,32 +413,118 @@ protected void doTestSteps() clickLinkWithText("Originating Location Specimen Lists"); assertTextPresent("KCMC, Moshi, Tanzania"); checkCheckbox("notify"); - String specimen1 = getText(Locator.xpath("//tr[@class = 'labkey-alternate-row']/td[3]//td")); + _specimen1 = getText(Locator.xpath("//tr[@class = 'labkey-alternate-row']/td[3]//td")); checkCheckbox("notify", 4); - String specimen2 = getText(Locator.xpath("//tr[@class = 'labkey-row']/td[3]//td")); + _specimen2 = getText(Locator.xpath("//tr[@class = 'labkey-row']/td[3]//td")); checkCheckbox("sendXls"); checkCheckbox("sendTsv"); clickNavButton("Send Email"); + _requestId = Integer.parseInt(getUrlParam(getURL().toString(), "id", false)); clickLinkWithText("Providing Location Specimen Lists"); assertTextPresent("Contract Lab Services, Johannesburg, South Africa (Repository)"); clickNavButton("Cancel"); - // cancel request + } + + private String _specimen1; + private String _specimen2; + private final static String ATTACHMENT1 = "KCMC_Moshi_Ta_to_Aurum_Health_.tsv"; + private final static String ATTACHMENT2 = "KCMC_Moshi_Ta_to_Aurum_Health_KO_%s.xls"; // Params: date(yyyy-MM-dd) + private final String NOTIFICATION_TEMPLATE = // Params: Study Name, requestId, Study Name, requestId, Username, Date(yyyy-MM-dd) + "%s: Specimen Request Notification \n" + + " \n" + + " Specimen request #%s was updated in %s. \n" + + " \n" + + " \n" + + " Request Details Specimen Request %s Destination Aurum Health KOSH Lab, Orkney, South Africa (Repository) Status New Request Modified by %s Action Originating location notification of specimen shipment to Aurum Health KOSH Lab, Orkney, South Africa (Repository) Attachments KCMC_Moshi_Ta_to_Aurum_Health_.tsv \n" + + " KCMC_Moshi_Ta_to_Aurum_Health_KO_%s.xls \n" + + " Assay Plan:\n" + + " Assay Plan\n" + + " \n" + + " Shipping Information:\n" + + " Shipping\n" + + " \n" + + " Comments:\n" + + " Comments\n" + + " \n" + + " Last One:\n" + + " sample last one input \n" + + " Specimen List (Request Link)\n" + + "\n" + + " \n" + + " ; Participant Id Global Unique Id Visit Description Visit Volume Volume Units Primary Type Derivative Type Additive Type Derivative Type2 Sub Additive Derivative Draw Timestamp Clinic Processing Location First Processed By Initials Sal Receipt Date Class Id Protocol Number Primary Volume Primary Volume Units Total Cell Count Tube Type Comments Locked In Request Requestable Site Name Site Ldms Code At Repository Available Availability Reason Quality Control Flag Quality Control Comments Collection Cohort Vial Count Locked In Request Count At Repository Count Available Count Expected Available Count 1 999320824 BAA07XNP-01 Vst 1.0 ML Blood (Whole) Plasma, Unknown Processing EDTA N/A 2005-12-23 10:05:00.0 KCMC, Moshi, Tanzania Contract Lab Services, Johannesburg, South Africa LK 2005-12-23 00:00:00.0 LABK 39 15ml Cryovial true Contract Lab Services, Johannesburg, South Africa 350 true false This vial is unavailable because it is locked in a specimen request. false 2 1 2 0 1 2 999320087 CAA07XN8-01 Vst 1.0 ML Vaginal Swab Swab None N/A 2005-12-22 12:50:00.0 KCMC, Moshi, Tanzania Contract Lab Services, Johannesburg, South Africa LK 2005-12-22 00:00:00.0 LABK 39 15ml Cryovial true Contract Lab Services, Johannesburg, South Africa 350 true false This vial is unavailable because it is locked in a specimen request. false 1 1 1 0 0 3 999320706 DAA07YGW-01 Vst 1.0 ML Vaginal Swab Swab None N/A 2006-01-05 10:00:00.0 KCMC, Moshi, Tanzania Contract Lab Services, Johannesburg, South Africa LK 2006-01-05 00:00:00.0 LABK 39 15ml Cryovial true Contract Lab Services, Johannesburg, South Africa 350 true false This vial is unavailable because it is locked in a specimen request. false 1 1 1 0 0 4 999320898 FAA07XLJ-01 Vst 1.0 ML Vaginal Swab Swab None N/A 2005-12-20 12:05:00.0 KCMC, Moshi, Tanzania Contract Lab Services, Johannesburg, South Africa LK 2005-12-20 00:00:00.0 LABK 39 15ml Cryovial true Contract Lab Services, Johannesburg, South Africa 350 true false This vial is unavailable because it is locked in a specimen request. false 1 1 1 0 0 5 999320264 FAA07YSC-01 Vst 1.0 ML Vaginal Swab Swab None N/A 2006-01-13 12:10:00.0 KCMC, Moshi, Tanzania Contract Lab Services, Johannesburg, South Africa LK 2006-01-13 00:00:00.0 LABK 39 15ml Cryovial true Contract Lab Services, Johannesburg, South Africa 350 true false This vial is unavailable because it is locked in a specimen request. false 1 1 1 0 0 6 999320520 FAA07YXY-01 Vst 1.0 ML Vaginal Swab Swab None N/A 2005-12-15 10:30:00.0 KCMC, Moshi, Tanzania Contract Lab Services, Johannesburg, South Africa LK 2006-01-15 00:00:00.0 LABK 39 15ml Cryovial true Contract Lab Services, Johannesburg, South Africa 350 true false This vial is unavailable because it is locked in a specimen request. false 1 1 1 0 0 7 999320498 JAA07YJB-01 Vst 1.0 ML Vaginal Swab Swab None N/A 2006-01-05 09:30:00.0 KCMC, Moshi, Tanzania Contract Lab Services, Johannesburg, South Africa LK 2006-01-11 00:00:00.0 LABK 39 15ml Cryovial true Contract Lab Services, Johannesburg, South Africa 350 true false This vial is unavailable because it is locked in a specimen request. false 1 1 1 0 0 8 999320476 JAA07YSQ-01 Vst 1.0 ML Vaginal Swab Swab None N/A 2006-01-11 10:20:00.0 KCMC, Moshi, Tanzania Contract Lab Services, Johannesburg, South Africa LK 2006-01-11 00:00:00.0 LABK 39 15ml Cryovial true Contract Lab Services, Johannesburg, South Africa 350 true false This vial is unavailable because it is locked in a specimen request. false 1 1 1 0 0 9 999320980 KAA07YV1-01 Vst 1.0 ML Vaginal Swab Swab None N/A 2006-01-17 08:30:00.0 KCMC, Moshi, Tanzania Contract Lab Services, Johannesburg, South Africa LK 2006-01-17 00:00:00.0 LABK 39 15ml Cryovial true Contract Lab Services, Johannesburg, South Africa 350 true false This vial is unavailable because it is locked in a specimen request. false 1 1 1 0 0 10 999320520 KAA07YY0-01 Vst 1.0 ML Blood (Whole) Plasma, Unknown Processing EDTA N/A 2005-12-15 10:30:00.0 KCMC, Moshi, Tanzania Contract Lab Services, Johannesburg, South Africa LK 2006-01-15 00:00:00.0 LABK 39 15ml Cryovial true Contract Lab Services, Johannesburg, South Africa 350 true false This vial is unavailable because it is locked in a specimen request. false 2 2 2 0 0 11 999320520 KAA07YY0-02 Vst 1.0 ML Blood (Whole) Plasma, Unknown Processing EDTA N/A 2005-12-15 10:30:00.0 KCMC, Moshi, Tanzania Contract Lab Services, Johannesburg, South Africa LK 2006-01-15 00:00:00.0 LABK 39 15ml Cryovial true Contract Lab Services, Johannesburg, South Africa 350 true false This vial is unavailable because it is locked in a specimen request. false 2 2 2 0 0"; + private void verifyNotificationEmails() + { + String date = new SimpleDateFormat("yyyy-MM-dd").format(new Date()); + String notification = String.format(NOTIFICATION_TEMPLATE, getStudyLabel(), _requestId, getStudyLabel(), _requestId, PasswordUtil.getUsername(), date); + + log("Check notification emails"); + goToModule("Dumbster"); + assertTextPresent("Specimen Request Notification", 8); + assertTextPresent(USER1, 4); + assertTextPresent(USER2, 4); + + log("Check for correct data in notification emails"); + if (getTableCellText("dataregion_EmailRecord", 2, 0).equals(USER1)) + { + clickLinkContainingText("Specimen Request Notification", false); + assertTextPresent(_specimen1); + assertTextNotPresent(_specimen2); + clickLinkContainingText("Specimen Request Notification", 1, false); + assertTextPresent(_specimen2); + } + else + { + clickLinkContainingText("Specimen Request Notification", false); + assertTextPresent(_specimen2); + assertTextNotPresent(_specimen1); + clickLinkContainingText("Specimen Request Notification", 1, false); + assertTextPresent(_specimen1); + } + + DataRegionTable mailTable = new DataRegionTable("EmailRecord", this, false, false); + assertEquals("Notification was not as expected", notification, mailTable.getDataAsText(1, "Message")); + String attachment1 = getAttribute(Locator.linkWithText(ATTACHMENT1), "href"); + String attachment2 = getAttribute(Locator.linkWithText(String.format(ATTACHMENT2, date)), "href"); + + try + { + assertEquals("Bad link to attachment: " + ATTACHMENT1, HttpStatus.SC_OK, WebTestHelper.getHttpGetResponse(attachment1)); + assertEquals("Bad link to attachment: " + String.format(ATTACHMENT2, date), HttpStatus.SC_OK, WebTestHelper.getHttpGetResponse(attachment2)); + } + catch (HttpException e) + { + Assert.fail("Failed to get HTTP client: "+e.getMessage()); + } + catch (IOException e) + { + Assert.fail("Failed to perform HTTP GET: "+e.getMessage()); + } + + clickLinkWithText("Request Link"); + assertTextPresent("Specimen Request " + _requestId); + } + + private void verifyRequestCancel() + { clickLinkWithText("Update Request"); selectOptionByText("status", "Not Yet Submitted"); clickNavButton("Save Changes and Send Notifications"); clickNavButton("Cancel Request", 0); Assert.assertTrue(getConfirmationAndWait().matches("^Canceling will permanently delete this pending request\\. Continue[\\s\\S]$")); assertTextPresent("No data to show."); - clickLinkWithText(STUDY_NAME); + clickLinkWithText(getStudyLabel()); clickLinkWithText("Specimen Data"); clickLinkWithText("Vials by Derivative", false); waitAndClick(WAIT_FOR_JAVASCRIPT, Locator.linkWithText("Swab"), WAIT_FOR_PAGE); checkCheckbox(".toggle"); clickMenuButton("Request Options", "Create New Request"); clickNavButton("Cancel"); + } + private void verifyReports() + { log("check reports by participant group"); - clickLinkWithText("My Study"); + clickLinkWithText(getFolderName()); clickLinkWithText("Specimen Data"); clickLinkWithText("Vials by Primary Type", false); waitAndClick(WAIT_FOR_JAVASCRIPT, Locator.linkWithText("Blood (Whole)"), WAIT_FOR_PAGE); @@ -433,42 +550,15 @@ protected void doTestSteps() clickNavButton("Refresh"); assertLinkPresentWithTextCount(PTIDS[0], 3); assertLinkPresentWithTextCount(PTIDS[1], 5); - - log("Check notification emails"); - goToModule("Dumbster"); - assertTextPresent("Specimen Request Notification", 8); - assertTextPresent(USER1, 4); - assertTextPresent(USER2, 4); - - log("Check for correct data in notification emails"); - if ( getTableCellText("dataregion_EmailRecord", 2, 0).equals(USER1)) - { - clickLinkContainingText("Specimen Request Notification", false); - assertTextPresent(specimen1); - assertTextNotPresent(specimen2); - clickLinkContainingText("Specimen Request Notification", 1, false); - assertTextPresent(specimen2); - } - else - { - clickLinkContainingText("Specimen Request Notification", false); - assertTextPresent(specimen2); - assertTextNotPresent(specimen1); - clickLinkContainingText("Specimen Request Notification", 1, false); - assertTextPresent(specimen1); - } - - exportSpecimenTest(); - searchTest(); } private void searchTest() { goToProjectHome(); - clickLinkContainingText(FOLDER_NAME); + clickLinkContainingText(getFolderName()); clickTab("Specimen Data"); waitForPageToLoad(); - waitForExt4(); + waitForVialSearch(); Ext4FieldRef additiveType = Ext4FieldRef.getForLabel(this, "Additive Type"); additiveType.setValue("Heparin"); Ext4FieldRef.getForLabel(this, "Participant").setValue("999320812"); @@ -476,20 +566,20 @@ private void searchTest() assertTextNotPresent("Serum Separator"); assertTextPresent("(ParticipantId = 999320812) AND (AdditiveType = Heparin)"); goBack(); - waitForExt4(); + waitForVialSearch(); additiveType.setValue(new String[] {"Heparin", "Ammounium Heparin"}); clickButtonContainingText("Search"); assertTextPresent("ONE OF"); goBack(); - waitForExt4(); + waitForVialSearch(); additiveType.setValue(new String[] {"Ammonium Heparin","Cell Preparation Tube Heparin","Cell Preparation Tube SCI","Citrate Phosphate Dextrose","EDTA","Fetal Fibronectin Buffer","Guanidine Isothiocyanate (GITC)","Heparin","Liquid Potassium EDTA","Liquid Sodium EDTA","Lithium Heparin","Lithium Heparin and Gel for Plasma","None","Normal Saline","Optimum Cutting Temperature Medium","Orasure Collection Container","Other","PAXgene Blood RNA tube","Phosphate Buffered Saline","Plasma Preparation Tube","PLP Fixative","Port-a-cul Transport Tube","Potassium EDTA","RNA Later","Serum Separator","Sodium Citrate","Sodium EDTA","Sodium Fluoride","Sodium Fluoride/Potassium Oxalate","Sodium Heparin","Sodium Polyanetholesulfonate","Spray Dried Potassium EDTA","Spray Dried Sodium EDTA","Thrombin","Tissue Freezing Medium","Unknown Additive","Viral Transport Media"}); clickButtonContainingText("Search"); assertTextPresent("IS NOT ANY OF "); } - private void waitForExt4() + private void waitForVialSearch() { - sleep(2000); //TODO: this needs something better + waitForElement(Locator.css(".specimenSearchLoaded")); } private void exportSpecimenTest() @@ -506,13 +596,12 @@ private void exportSpecimenTest() DataRegionTable auditTable = new DataRegionTable("audit", this); String[][] columnAndValues = new String[][] {{"Created By", getDisplayName()}, - {"Project", PROJECT_NAME}, {"Container", FOLDER_NAME}, {"SchemaName", "study"}, + {"Project", PROJECT_NAME}, {"Container", getFolderName()}, {"SchemaName", "study"}, {"QueryName", SPECIMEN_DETAIL}, {"Comment", "Exported to Excel Web Query data"}}; for(String[] columnAndValue : columnAndValues) { log("Checking column: "+ columnAndValue[0]); Assert.assertEquals(columnAndValue[1], auditTable.getDataAsText(0, columnAndValue[0])); } - //To change body of created methods use File | Settings | File Templates. } } From 41df3a1b6b54b192354ba9d18443c7e31db6dbf8 Mon Sep 17 00:00:00 2001 From: Trey Chadick Date: Wed, 26 Sep 2012 21:34:56 +0000 Subject: [PATCH 062/494] Refactor redundant *NavButton helpers SVN r22196 |2012-09-26 21:34:56 +0000 --- src/org/labkey/test/tests/SpecimenTest.java | 80 ++++++++++----------- 1 file changed, 39 insertions(+), 41 deletions(-) diff --git a/src/org/labkey/test/tests/SpecimenTest.java b/src/org/labkey/test/tests/SpecimenTest.java index f279311c..d65eb3ac 100644 --- a/src/org/labkey/test/tests/SpecimenTest.java +++ b/src/org/labkey/test/tests/SpecimenTest.java @@ -17,10 +17,8 @@ package org.labkey.test.tests; import org.junit.Assert; -import org.labkey.test.BaseSeleniumWebTest; import org.apache.http.HttpException; import org.apache.http.HttpStatus; -import org.apache.james.mime4j.field.datetime.DateTime; import org.labkey.test.Locator; import org.labkey.test.WebTestHelper; import org.labkey.test.util.DataRegionTable; @@ -74,10 +72,10 @@ protected void doCreateSteps() enableEmailRecorder(); initializeFolder(); - clickNavButton("Create Study"); + clickButton("Create Study"); setFormElement(Locator.name("label"), getStudyLabel()); click(Locator.radioButtonByNameAndValue("simpleRepository", "false")); - clickNavButton("Create Study"); + clickButton("Create Study"); setPipelineRoot(getPipelinePath()); @@ -139,7 +137,7 @@ private void createParticipantGroups() clickLinkWithText("Manage Study"); clickLinkWithText("Manage Participant Groups"); log("Set up participant groups"); - clickNavButton("Create", 0); + clickButton("Create", 0); ExtHelper.waitForExtDialog(this, "Define Participant Group"); setFormElement("groupLabel", "Category1"); setFormElement("categoryIdentifiers", PTIDS[0] + "," + PTIDS[1]); @@ -176,16 +174,16 @@ private void setupSpecimenManagement() clickLinkWithText("Manage Study"); clickLinkWithText("Manage Request Statuses"); setFormElement("newLabel", "New Request"); - clickNavButton("Save"); + clickButton("Save"); setFormElement("newLabel", "Processing"); - clickNavButton("Save"); + clickButton("Save"); setFormElement("newLabel", "Completed"); checkCheckbox("newFinalState"); - clickNavButton("Save"); + clickButton("Save"); setFormElement("newLabel", "Rejected"); checkCheckbox("newFinalState"); uncheckCheckbox("newSpecimensLocked"); - clickNavButton("Done"); + clickButton("Done"); } private void setupActorsAndGroups() @@ -193,14 +191,14 @@ private void setupActorsAndGroups() clickLinkWithText("Manage Actors and Groups"); setFormElement("newLabel", "SLG"); selectOptionByText("newPerSite", "One Per Study"); - clickNavButton("Save"); + clickButton("Save"); clickLinkWithText("Update Members"); setText("names", USER1); uncheckCheckbox("sendEmail"); - clickNavButton("Update Members"); + clickButton("Update Members"); setFormElement("newLabel", "IRB"); selectOptionByText("newPerSite", "Multiple Per Study (Location Affiliated)"); - clickNavButton("Save"); + clickButton("Save"); clickLinkWithText("Update Members", 1); clickLinkWithText(DESTINATION_SITE); setText("names", USER2); @@ -215,7 +213,7 @@ private void setupDefaultRequirements() clickLinkWithText("Manage Default Requirements"); selectOptionByText("originatorActor", "IRB"); setFormElement("originatorDescription", "Originating IRB Approval"); - clickNavButton("Add Requirement"); + clickButton("Add Requirement"); selectOptionByText("providerActor", "IRB"); setFormElement("providerDescription", "Providing IRB Approval"); clickLink(Locator.xpath("//input[@name='providerDescription']/../.." + Locator.navButton("Add Requirement").getPath())); @@ -231,11 +229,11 @@ private void setupDefaultRequirements() private void setupRequestForm() { clickLinkWithText("Manage New Request Form"); - clickNavButton("Add New Input", 0); + clickButton("Add New Input", 0); setFormElement("//descendant::input[@name='title'][4]", "Last One"); setFormElement("//descendant::input[@name='helpText'][4]", "A test input"); click(Locator.xpath("//descendant::input[@name='required'][4]")); - clickNavButton("Save"); + clickButton("Save"); clickLinkWithText(getStudyLabel()); } @@ -247,7 +245,7 @@ private void setupActorNotification() clickLinkWithText("Manage Notifications"); assertTextPresent("Default Email Recipients"); checkRadioButton("defaultEmailNotify", "All"); - clickNavButton("Save"); + clickButton("Save"); } private void uploadSpecimensFromFile() @@ -261,20 +259,20 @@ private void uploadSpecimensFromFile() setFormElement("input2", "Comments"); setFormElement("input1", "Shipping"); setFormElement("input3", "Last one"); - clickNavButton("Create and View Details"); + clickButton("Create and View Details"); clickLinkWithText("Upload Specimen Ids"); setFormElement(Locator.xpath("//textarea[@id='tsv3']"), "AAA07XK5-01"); // add specimen - clickNavButton("Submit"); // Submit button + clickButton("Submit"); // Submit button clickLinkWithText("Upload Specimen Ids"); setFormElement(Locator.xpath("//textarea[@id='tsv3']"), "AAA07XK5-01"); // try to add again - clickNavButton("Submit", 0); // Submit button + clickButton("Submit", 0); // Submit button waitForText("Specimen AAA07XK5-01 not available", 20000); setFormElement(Locator.xpath("//textarea[@id='tsv3']"), "AAA07XK5-02"); // try to add one that doesn't exist - clickNavButton("Submit", 0); // Submit button + clickButton("Submit", 0); // Submit button waitForText("Specimen AAA07XK5-02 not available", 20000); setFormElement(Locator.xpath("//textarea[@id='tsv3']"), "AAA07XK5-04\nAAA07XK5-06\nAAA07XSF-03"); // add different one - clickNavButton("Submit"); // Submit button + clickButton("Submit"); // Submit button } @Override @@ -302,14 +300,14 @@ private void verifyActorDetails() List checkedCheckBoxes = findAllMatches(Locator.xpath("//input[@type='checkbox' and @name='notificationIdPairs' and @checked]")); List disabledCheckBoxes = findAllMatches(Locator.xpath("//input[@type='checkbox' and @name='notificationIdPairs' and @disabled]")); Assert.assertTrue("Actor Notification: All actors should be notified if addresses configured.", allCheckBoxes.size() == checkedCheckBoxes.size() + disabledCheckBoxes.size()); - clickNavButton("Cancel"); + clickButton("Cancel"); } assertTextPresent("Associated Specimens"); assertTextPresent("AAA07XK5-01", "AAA07XK5-04", "AAA07XK5-06", "AAA07XSF-03"); - clickNavButton("Cancel Request"); + clickButton("Cancel Request"); Assert.assertTrue(getConfirmationAndWait().matches("^Canceling will permanently delete this pending request\\. Continue[\\s\\S]$")); } @@ -335,10 +333,10 @@ private void createRequest() setFormElement("input0", "Assay Plan"); setFormElement("input2", "Comments"); setFormElement("input1", "Shipping"); - clickNavButton("Create and View Details"); + clickButton("Create and View Details"); assertTextPresent("Please provide all required input."); setFormElement("input3", "sample last one input"); - clickNavButton("Create and View Details"); + clickButton("Create and View Details"); assertTextPresent("sample last one input"); assertTextPresent("IRB"); assertTextPresent("KCMC, Moshi, Tanzania"); @@ -364,11 +362,11 @@ private void createRequest() clickMenuButtonAndContinue("Request Options", "Add To Existing Request"); ExtHelper.waitForExtDialog(this, "Request Vial", WAIT_FOR_JAVASCRIPT); waitForElement(Locator.css("#request-vial-details .x-grid3-row")); - clickNavButton("Add 8 Vials to Request", 0); + clickButton("Add 8 Vials to Request", 0); ExtHelper.waitForExtDialog(this, "Success", WAIT_FOR_JAVASCRIPT * 5); - clickNavButton("OK", 0); + clickButton("OK", 0); clickMenuButton("Request Options", "View Existing Requests"); - clickNavButton("Details"); + clickButton("Details"); assertTextPresent("sample last one input"); assertTextPresent("IRB"); assertTextPresent("KCMC, Moshi, Tanzania"); @@ -385,7 +383,7 @@ private void createRequest() // submit request assertTextPresent("Not Yet Submitted"); assertTextNotPresent("New Request"); - clickNavButton("Submit Request", 0); + clickButton("Submit Request", 0); Assert.assertTrue(getConfirmationAndWait().matches("^Once a request is submitted, its specimen list may no longer be modified\\. Continue[\\s\\S]$")); assertTextNotPresent("Not Yet Submitted"); assertTextPresent("New Request"); @@ -393,12 +391,12 @@ private void createRequest() // modify request selectOptionByText("newActor", "SLG"); setFormElement("newDescription", "Other SLG Approval"); - clickNavButton("Add Requirement"); + clickButton("Add Requirement"); clickLinkWithText("Details"); checkCheckbox("complete"); checkCheckbox("notificationIdPairs"); checkCheckbox("notificationIdPairs", 1); - clickNavButton("Save Changes and Send Notifications"); + clickButton("Save Changes and Send Notifications"); assertTextPresent("Complete"); } @@ -418,11 +416,11 @@ private void verifyViews() _specimen2 = getText(Locator.xpath("//tr[@class = 'labkey-row']/td[3]//td")); checkCheckbox("sendXls"); checkCheckbox("sendTsv"); - clickNavButton("Send Email"); + clickButton("Send Email"); _requestId = Integer.parseInt(getUrlParam(getURL().toString(), "id", false)); clickLinkWithText("Providing Location Specimen Lists"); assertTextPresent("Contract Lab Services, Johannesburg, South Africa (Repository)"); - clickNavButton("Cancel"); + clickButton("Cancel"); } private String _specimen1; @@ -508,8 +506,8 @@ private void verifyRequestCancel() { clickLinkWithText("Update Request"); selectOptionByText("status", "Not Yet Submitted"); - clickNavButton("Save Changes and Send Notifications"); - clickNavButton("Cancel Request", 0); + clickButton("Save Changes and Send Notifications"); + clickButton("Cancel Request", 0); Assert.assertTrue(getConfirmationAndWait().matches("^Canceling will permanently delete this pending request\\. Continue[\\s\\S]$")); assertTextPresent("No data to show."); clickLinkWithText(getStudyLabel()); @@ -518,7 +516,7 @@ private void verifyRequestCancel() waitAndClick(WAIT_FOR_JAVASCRIPT, Locator.linkWithText("Swab"), WAIT_FOR_PAGE); checkCheckbox(".toggle"); clickMenuButton("Request Options", "Create New Request"); - clickNavButton("Cancel"); + clickButton("Cancel"); } private void verifyReports() @@ -530,24 +528,24 @@ private void verifyReports() waitAndClick(WAIT_FOR_JAVASCRIPT, Locator.linkWithText("Blood (Whole)"), WAIT_FOR_PAGE); pushLocation(); clickLinkWithText("Reports"); - clickNavButton("View"); // Summary Report + clickButton("View"); // Summary Report //Verify by vial count assertElementPresent(Locator.xpath("//a[number(text()) > 0]"), 36); selectOptionByText("participantGroupFilter", "Category1"); - clickNavButton("Refresh"); + clickButton("Refresh"); assertElementNotPresent(Locator.xpath("//a[number(text()) > 6]")); assertElementPresent(Locator.xpath("//a[number(text()) <= 6]"), 8); selectOptionByText("participantGroupFilter", "All Groups"); - clickNavButton("Refresh"); + clickButton("Refresh"); assertElementPresent(Locator.xpath("//a[number(text()) > 0]"), 36); //Verify by ptid list checkCheckbox("viewPtidList"); uncheckCheckbox("viewVialCount"); - clickNavButton("Refresh"); + clickButton("Refresh"); assertLinkPresentWithTextCount(PTIDS[0], 3); assertLinkPresentWithTextCount(PTIDS[1], 5); selectOptionByText("participantGroupFilter", "Category1"); - clickNavButton("Refresh"); + clickButton("Refresh"); assertLinkPresentWithTextCount(PTIDS[0], 3); assertLinkPresentWithTextCount(PTIDS[1], 5); } From 8ee1ff982d4659ab78095b05b2cd12dd63c02c8d Mon Sep 17 00:00:00 2001 From: Karl Lum Date: Thu, 27 Sep 2012 04:58:18 +0000 Subject: [PATCH 063/494] Merge itn12.2 branch changes through r22190 (which includes release 12.2 changes through r22150) SVN r22204 |2012-09-27 04:58:18 +0000 --- src/org/labkey/test/tests/SpecimenTest.java | 84 ++++++++++++--------- 1 file changed, 48 insertions(+), 36 deletions(-) diff --git a/src/org/labkey/test/tests/SpecimenTest.java b/src/org/labkey/test/tests/SpecimenTest.java index d65eb3ac..40f9f17b 100644 --- a/src/org/labkey/test/tests/SpecimenTest.java +++ b/src/org/labkey/test/tests/SpecimenTest.java @@ -92,6 +92,19 @@ protected void doCreateSteps() uploadSpecimensFromFile(); } + @Override + protected void doVerifySteps() + { + verifyActorDetails(); + createRequest(); + verifyViews(); + verifyNotificationEmails(); + verifyRequestCancel(); + verifyReports(); + exportSpecimenTest(); + searchTest(); + } + private void setupRequestabilityRules() { // Create custom query to test requestability rules. @@ -124,9 +137,9 @@ private void setupRequestabilityRules() assertElementPresent(Locator.xpath("//table[@id='btn_moveDown' and contains(@class, 'x-item-disabled')]")); clickButton("Add Rule", 0); click(Locator.menuItem("Custom Query")); - ExtHelper.selectComboBoxItem(this, Locator.xpath("//div[@id='x-form-el-userQuery_schema']"), "study" ); - ExtHelper.selectComboBoxItem(this, Locator.xpath("//div[@id='x-form-el-userQuery_query']"), REQUESTABILITY_QUERY ); - ExtHelper.selectComboBoxItem(this, Locator.xpath("//div[@id='x-form-el-userQuery_action']"), "Unavailable" ); + _extHelper.selectComboBoxItem(Locator.xpath("//div[@id='x-form-el-userQuery_schema']"), "study" ); + _extHelper.selectComboBoxItem(Locator.xpath("//div[@id='x-form-el-userQuery_query']"), REQUESTABILITY_QUERY ); + _extHelper.selectComboBoxItem(Locator.xpath("//div[@id='x-form-el-userQuery_action']"), "Unavailable" ); clickButton("Submit",0); clickButton("Save"); } @@ -138,10 +151,10 @@ private void createParticipantGroups() clickLinkWithText("Manage Participant Groups"); log("Set up participant groups"); clickButton("Create", 0); - ExtHelper.waitForExtDialog(this, "Define Participant Group"); + _extHelper.waitForExtDialog("Define Participant Group"); setFormElement("groupLabel", "Category1"); setFormElement("categoryIdentifiers", PTIDS[0] + "," + PTIDS[1]); - ExtHelper.clickExtButton(this, "Define Participant Group", "Save", 0); + _extHelper.clickExtButton("Define Participant Group", "Save", 0); } private void checkTubeType() @@ -275,19 +288,6 @@ private void uploadSpecimensFromFile() clickButton("Submit"); // Submit button } - @Override - protected void doVerifySteps() - { - verifyActorDetails(); - createRequest(); - verifyViews(); - verifyNotificationEmails(); - verifyRequestCancel(); - verifyReports(); - exportSpecimenTest(); - searchTest(); - } - private void verifyActorDetails() { // Check each Actor's Details for "Default Actor Notification" feature; @@ -360,10 +360,10 @@ private void createRequest() waitAndClick(WAIT_FOR_JAVASCRIPT, Locator.linkWithText("Swab"), WAIT_FOR_PAGE); checkCheckbox(".toggle"); clickMenuButtonAndContinue("Request Options", "Add To Existing Request"); - ExtHelper.waitForExtDialog(this, "Request Vial", WAIT_FOR_JAVASCRIPT); + _extHelper.waitForExtDialog("Request Vial", WAIT_FOR_JAVASCRIPT); waitForElement(Locator.css("#request-vial-details .x-grid3-row")); clickButton("Add 8 Vials to Request", 0); - ExtHelper.waitForExtDialog(this, "Success", WAIT_FOR_JAVASCRIPT * 5); + _extHelper.waitForExtDialog("Success", WAIT_FOR_JAVASCRIPT * 5); clickButton("OK", 0); clickMenuButton("Request Options", "View Existing Requests"); clickButton("Details"); @@ -409,11 +409,21 @@ private void verifyViews() assertTextPresent(USER2); clickLinkWithText("View Request"); clickLinkWithText("Originating Location Specimen Lists"); - assertTextPresent("KCMC, Moshi, Tanzania"); - checkCheckbox("notify"); - _specimen1 = getText(Locator.xpath("//tr[@class = 'labkey-alternate-row']/td[3]//td")); - checkCheckbox("notify", 4); - _specimen2 = getText(Locator.xpath("//tr[@class = 'labkey-row']/td[3]//td")); + // Ordering of locations is nondeterministic + if (isPresentInThisOrder("The McMichael Lab, Oxford, UK", "KCMC, Moshi, Tanzania") == null) + { + checkCheckbox("notify"); // MCMichael - SLG + _specimen_McMichael = getText(Locator.xpath("//tr[@class = 'labkey-alternate-row']/td[3]//td")); + checkCheckbox("notify", 4); // KCMC - IRB, Aurum Health KOSH + _specimen_KCMC = getText(Locator.xpath("//tr[@class = 'labkey-row']/td[3]//td")); + } + else + { + checkCheckbox("notify", 1); // KCMC - IRB, Aurum Health KOSH + _specimen_KCMC = getText(Locator.xpath("//tr[@class = 'labkey-alternate-row']/td[3]//td")); + checkCheckbox("notify", 3); // MCMichael - SLG + _specimen_McMichael = getText(Locator.xpath("//tr[@class = 'labkey-row']/td[3]//td")); + } checkCheckbox("sendXls"); checkCheckbox("sendTsv"); clickButton("Send Email"); @@ -423,8 +433,8 @@ private void verifyViews() clickButton("Cancel"); } - private String _specimen1; - private String _specimen2; + private String _specimen_McMichael; + private String _specimen_KCMC; private final static String ATTACHMENT1 = "KCMC_Moshi_Ta_to_Aurum_Health_.tsv"; private final static String ATTACHMENT2 = "KCMC_Moshi_Ta_to_Aurum_Health_KO_%s.xls"; // Params: date(yyyy-MM-dd) private final String NOTIFICATION_TEMPLATE = // Params: Study Name, requestId, Study Name, requestId, Username, Date(yyyy-MM-dd) @@ -449,7 +459,7 @@ private void verifyViews() " Specimen List (Request Link)\n" + "\n" + " \n" + - " ; Participant Id Global Unique Id Visit Description Visit Volume Volume Units Primary Type Derivative Type Additive Type Derivative Type2 Sub Additive Derivative Draw Timestamp Clinic Processing Location First Processed By Initials Sal Receipt Date Class Id Protocol Number Primary Volume Primary Volume Units Total Cell Count Tube Type Comments Locked In Request Requestable Site Name Site Ldms Code At Repository Available Availability Reason Quality Control Flag Quality Control Comments Collection Cohort Vial Count Locked In Request Count At Repository Count Available Count Expected Available Count 1 999320824 BAA07XNP-01 Vst 1.0 ML Blood (Whole) Plasma, Unknown Processing EDTA N/A 2005-12-23 10:05:00.0 KCMC, Moshi, Tanzania Contract Lab Services, Johannesburg, South Africa LK 2005-12-23 00:00:00.0 LABK 39 15ml Cryovial true Contract Lab Services, Johannesburg, South Africa 350 true false This vial is unavailable because it is locked in a specimen request. false 2 1 2 0 1 2 999320087 CAA07XN8-01 Vst 1.0 ML Vaginal Swab Swab None N/A 2005-12-22 12:50:00.0 KCMC, Moshi, Tanzania Contract Lab Services, Johannesburg, South Africa LK 2005-12-22 00:00:00.0 LABK 39 15ml Cryovial true Contract Lab Services, Johannesburg, South Africa 350 true false This vial is unavailable because it is locked in a specimen request. false 1 1 1 0 0 3 999320706 DAA07YGW-01 Vst 1.0 ML Vaginal Swab Swab None N/A 2006-01-05 10:00:00.0 KCMC, Moshi, Tanzania Contract Lab Services, Johannesburg, South Africa LK 2006-01-05 00:00:00.0 LABK 39 15ml Cryovial true Contract Lab Services, Johannesburg, South Africa 350 true false This vial is unavailable because it is locked in a specimen request. false 1 1 1 0 0 4 999320898 FAA07XLJ-01 Vst 1.0 ML Vaginal Swab Swab None N/A 2005-12-20 12:05:00.0 KCMC, Moshi, Tanzania Contract Lab Services, Johannesburg, South Africa LK 2005-12-20 00:00:00.0 LABK 39 15ml Cryovial true Contract Lab Services, Johannesburg, South Africa 350 true false This vial is unavailable because it is locked in a specimen request. false 1 1 1 0 0 5 999320264 FAA07YSC-01 Vst 1.0 ML Vaginal Swab Swab None N/A 2006-01-13 12:10:00.0 KCMC, Moshi, Tanzania Contract Lab Services, Johannesburg, South Africa LK 2006-01-13 00:00:00.0 LABK 39 15ml Cryovial true Contract Lab Services, Johannesburg, South Africa 350 true false This vial is unavailable because it is locked in a specimen request. false 1 1 1 0 0 6 999320520 FAA07YXY-01 Vst 1.0 ML Vaginal Swab Swab None N/A 2005-12-15 10:30:00.0 KCMC, Moshi, Tanzania Contract Lab Services, Johannesburg, South Africa LK 2006-01-15 00:00:00.0 LABK 39 15ml Cryovial true Contract Lab Services, Johannesburg, South Africa 350 true false This vial is unavailable because it is locked in a specimen request. false 1 1 1 0 0 7 999320498 JAA07YJB-01 Vst 1.0 ML Vaginal Swab Swab None N/A 2006-01-05 09:30:00.0 KCMC, Moshi, Tanzania Contract Lab Services, Johannesburg, South Africa LK 2006-01-11 00:00:00.0 LABK 39 15ml Cryovial true Contract Lab Services, Johannesburg, South Africa 350 true false This vial is unavailable because it is locked in a specimen request. false 1 1 1 0 0 8 999320476 JAA07YSQ-01 Vst 1.0 ML Vaginal Swab Swab None N/A 2006-01-11 10:20:00.0 KCMC, Moshi, Tanzania Contract Lab Services, Johannesburg, South Africa LK 2006-01-11 00:00:00.0 LABK 39 15ml Cryovial true Contract Lab Services, Johannesburg, South Africa 350 true false This vial is unavailable because it is locked in a specimen request. false 1 1 1 0 0 9 999320980 KAA07YV1-01 Vst 1.0 ML Vaginal Swab Swab None N/A 2006-01-17 08:30:00.0 KCMC, Moshi, Tanzania Contract Lab Services, Johannesburg, South Africa LK 2006-01-17 00:00:00.0 LABK 39 15ml Cryovial true Contract Lab Services, Johannesburg, South Africa 350 true false This vial is unavailable because it is locked in a specimen request. false 1 1 1 0 0 10 999320520 KAA07YY0-01 Vst 1.0 ML Blood (Whole) Plasma, Unknown Processing EDTA N/A 2005-12-15 10:30:00.0 KCMC, Moshi, Tanzania Contract Lab Services, Johannesburg, South Africa LK 2006-01-15 00:00:00.0 LABK 39 15ml Cryovial true Contract Lab Services, Johannesburg, South Africa 350 true false This vial is unavailable because it is locked in a specimen request. false 2 2 2 0 0 11 999320520 KAA07YY0-02 Vst 1.0 ML Blood (Whole) Plasma, Unknown Processing EDTA N/A 2005-12-15 10:30:00.0 KCMC, Moshi, Tanzania Contract Lab Services, Johannesburg, South Africa LK 2006-01-15 00:00:00.0 LABK 39 15ml Cryovial true Contract Lab Services, Johannesburg, South Africa 350 true false This vial is unavailable because it is locked in a specimen request. false 2 2 2 0 0"; + " Participant Id Global Unique Id Visit Description Visit Volume Volume Units Primary Type Derivative Type Additive Type Derivative Type2 Sub Additive Derivative Draw Timestamp Clinic Processing Location First Processed By Initials Sal Receipt Date Class Id Protocol Number Primary Volume Primary Volume Units Total Cell Count Tube Type Comments Locked In Request Requestable Site Name Site Ldms Code At Repository Available Availability Reason Quality Control Flag Quality Control Comments Collection Cohort Vial Count Locked In Request Count At Repository Count Available Count Expected Available Count 1 999320824 BAA07XNP-01 Vst 1.0 ML Blood (Whole) Plasma, Unknown Processing EDTA N/A 2005-12-23 10:05:00.0 KCMC, Moshi, Tanzania Contract Lab Services, Johannesburg, South Africa LK 2005-12-23 00:00:00.0 LABK 39 15ml Cryovial true Contract Lab Services, Johannesburg, South Africa 350 true false This vial is unavailable because it is locked in a specimen request. false 2 1 2 0 1 2 999320087 CAA07XN8-01 Vst 1.0 ML Vaginal Swab Swab None N/A 2005-12-22 12:50:00.0 KCMC, Moshi, Tanzania Contract Lab Services, Johannesburg, South Africa LK 2005-12-22 00:00:00.0 LABK 39 15ml Cryovial true Contract Lab Services, Johannesburg, South Africa 350 true false This vial is unavailable because it is locked in a specimen request. false 1 1 1 0 0 3 999320706 DAA07YGW-01 Vst 1.0 ML Vaginal Swab Swab None N/A 2006-01-05 10:00:00.0 KCMC, Moshi, Tanzania Contract Lab Services, Johannesburg, South Africa LK 2006-01-05 00:00:00.0 LABK 39 15ml Cryovial true Contract Lab Services, Johannesburg, South Africa 350 true false This vial is unavailable because it is locked in a specimen request. false 1 1 1 0 0 4 999320898 FAA07XLJ-01 Vst 1.0 ML Vaginal Swab Swab None N/A 2005-12-20 12:05:00.0 KCMC, Moshi, Tanzania Contract Lab Services, Johannesburg, South Africa LK 2005-12-20 00:00:00.0 LABK 39 15ml Cryovial true Contract Lab Services, Johannesburg, South Africa 350 true false This vial is unavailable because it is locked in a specimen request. false 1 1 1 0 0 5 999320264 FAA07YSC-01 Vst 1.0 ML Vaginal Swab Swab None N/A 2006-01-13 12:10:00.0 KCMC, Moshi, Tanzania Contract Lab Services, Johannesburg, South Africa LK 2006-01-13 00:00:00.0 LABK 39 15ml Cryovial true Contract Lab Services, Johannesburg, South Africa 350 true false This vial is unavailable because it is locked in a specimen request. false 1 1 1 0 0 6 999320520 FAA07YXY-01 Vst 1.0 ML Vaginal Swab Swab None N/A 2005-12-15 10:30:00.0 KCMC, Moshi, Tanzania Contract Lab Services, Johannesburg, South Africa LK 2006-01-15 00:00:00.0 LABK 39 15ml Cryovial true Contract Lab Services, Johannesburg, South Africa 350 true false This vial is unavailable because it is locked in a specimen request. false 1 1 1 0 0 7 999320498 JAA07YJB-01 Vst 1.0 ML Vaginal Swab Swab None N/A 2006-01-05 09:30:00.0 KCMC, Moshi, Tanzania Contract Lab Services, Johannesburg, South Africa LK 2006-01-11 00:00:00.0 LABK 39 15ml Cryovial true Contract Lab Services, Johannesburg, South Africa 350 true false This vial is unavailable because it is locked in a specimen request. false 1 1 1 0 0 8 999320476 JAA07YSQ-01 Vst 1.0 ML Vaginal Swab Swab None N/A 2006-01-11 10:20:00.0 KCMC, Moshi, Tanzania Contract Lab Services, Johannesburg, South Africa LK 2006-01-11 00:00:00.0 LABK 39 15ml Cryovial true Contract Lab Services, Johannesburg, South Africa 350 true false This vial is unavailable because it is locked in a specimen request. false 1 1 1 0 0 9 999320980 KAA07YV1-01 Vst 1.0 ML Vaginal Swab Swab None N/A 2006-01-17 08:30:00.0 KCMC, Moshi, Tanzania Contract Lab Services, Johannesburg, South Africa LK 2006-01-17 00:00:00.0 LABK 39 15ml Cryovial true Contract Lab Services, Johannesburg, South Africa 350 true false This vial is unavailable because it is locked in a specimen request. false 1 1 1 0 0 10 999320520 KAA07YY0-01 Vst 1.0 ML Blood (Whole) Plasma, Unknown Processing EDTA N/A 2005-12-15 10:30:00.0 KCMC, Moshi, Tanzania Contract Lab Services, Johannesburg, South Africa LK 2006-01-15 00:00:00.0 LABK 39 15ml Cryovial true Contract Lab Services, Johannesburg, South Africa 350 true false This vial is unavailable because it is locked in a specimen request. false 2 2 2 0 0 11 999320520 KAA07YY0-02 Vst 1.0 ML Blood (Whole) Plasma, Unknown Processing EDTA N/A 2005-12-15 10:30:00.0 KCMC, Moshi, Tanzania Contract Lab Services, Johannesburg, South Africa LK 2006-01-15 00:00:00.0 LABK 39 15ml Cryovial true Contract Lab Services, Johannesburg, South Africa 350 true false This vial is unavailable because it is locked in a specimen request. false 2 2 2 0 0"; private void verifyNotificationEmails() { String date = new SimpleDateFormat("yyyy-MM-dd").format(new Date()); @@ -465,22 +475,24 @@ private void verifyNotificationEmails() if (getTableCellText("dataregion_EmailRecord", 2, 0).equals(USER1)) { clickLinkContainingText("Specimen Request Notification", false); - assertTextPresent(_specimen1); - assertTextNotPresent(_specimen2); + assertTextPresent(_specimen_McMichael); + assertTextNotPresent(_specimen_KCMC); clickLinkContainingText("Specimen Request Notification", 1, false); - assertTextPresent(_specimen2); + assertTextPresent(_specimen_KCMC); + DataRegionTable mailTable = new DataRegionTable("EmailRecord", this, false, false); + assertEquals("Notification was not as expected", notification, mailTable.getDataAsText(1, "Message")); } else { clickLinkContainingText("Specimen Request Notification", false); - assertTextPresent(_specimen2); - assertTextNotPresent(_specimen1); + assertTextPresent(_specimen_KCMC); + assertTextNotPresent(_specimen_McMichael); clickLinkContainingText("Specimen Request Notification", 1, false); - assertTextPresent(_specimen1); + assertTextPresent(_specimen_McMichael); + DataRegionTable mailTable = new DataRegionTable("EmailRecord", this, false, false); + assertEquals("Notification was not as expected", notification, mailTable.getDataAsText(0, "Message")); } - DataRegionTable mailTable = new DataRegionTable("EmailRecord", this, false, false); - assertEquals("Notification was not as expected", notification, mailTable.getDataAsText(1, "Message")); String attachment1 = getAttribute(Locator.linkWithText(ATTACHMENT1), "href"); String attachment2 = getAttribute(Locator.linkWithText(String.format(ATTACHMENT2, date)), "href"); From f445710aa993f41b19a6d3f9526a9b7b788a1e2d Mon Sep 17 00:00:00 2001 From: Trey Chadick Date: Tue, 23 Oct 2012 21:29:06 +0000 Subject: [PATCH 064/494] Make participant group helpers more generic and use them in more tests SVN r22641 |2012-10-23 21:29:06 +0000 --- src/org/labkey/test/tests/SpecimenTest.java | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/org/labkey/test/tests/SpecimenTest.java b/src/org/labkey/test/tests/SpecimenTest.java index 40f9f17b..34e6ab21 100644 --- a/src/org/labkey/test/tests/SpecimenTest.java +++ b/src/org/labkey/test/tests/SpecimenTest.java @@ -146,15 +146,16 @@ private void setupRequestabilityRules() private void createParticipantGroups() { - clickLinkWithText(getFolderName()); - clickLinkWithText("Manage Study"); - clickLinkWithText("Manage Participant Groups"); - log("Set up participant groups"); - clickButton("Create", 0); - _extHelper.waitForExtDialog("Define Participant Group"); - setFormElement("groupLabel", "Category1"); - setFormElement("categoryIdentifiers", PTIDS[0] + "," + PTIDS[1]); - _extHelper.clickExtButton("Define Participant Group", "Save", 0); + _studyHelper.createCustomParticipantGroup(getProjectName(), getFolderName(), "Category1", "Participant", null, false, PTIDS[0], PTIDS[1]); +// clickLinkWithText(getFolderName()); +// clickLinkWithText("Manage Study"); +// clickLinkWithText("Manage Participant Groups"); +// log("Set up participant groups"); +// clickButton("Create", 0); +// _extHelper.waitForExtDialog("Define Participant Group"); +// setFormElement("groupLabel", "Category1"); +// setFormElement("categoryIdentifiers", PTIDS[0] + "," + PTIDS[1]); +// _extHelper.clickExtButton("Define Participant Group", "Save", 0); } private void checkTubeType() From 4d05c467798294a2c21a99dab6f46e73ab30bc2c Mon Sep 17 00:00:00 2001 From: Trey Chadick Date: Tue, 23 Oct 2012 21:31:38 +0000 Subject: [PATCH 065/494] Remove middle-man SVN r22642 |2012-10-23 21:31:38 +0000 --- src/org/labkey/test/tests/SpecimenTest.java | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/src/org/labkey/test/tests/SpecimenTest.java b/src/org/labkey/test/tests/SpecimenTest.java index 34e6ab21..3b63aa3e 100644 --- a/src/org/labkey/test/tests/SpecimenTest.java +++ b/src/org/labkey/test/tests/SpecimenTest.java @@ -82,7 +82,7 @@ protected void doCreateSteps() setupRequestabilityRules(); startSpecimenImport(1); waitForSpecimenImport(); - createParticipantGroups(); + _studyHelper.createCustomParticipantGroup(getProjectName(), getFolderName(), "Category1", "Participant", null, false, PTIDS[0], PTIDS[1]); checkTubeType(); setupSpecimenManagement(); setupActorsAndGroups(); @@ -144,20 +144,6 @@ private void setupRequestabilityRules() clickButton("Save"); } - private void createParticipantGroups() - { - _studyHelper.createCustomParticipantGroup(getProjectName(), getFolderName(), "Category1", "Participant", null, false, PTIDS[0], PTIDS[1]); -// clickLinkWithText(getFolderName()); -// clickLinkWithText("Manage Study"); -// clickLinkWithText("Manage Participant Groups"); -// log("Set up participant groups"); -// clickButton("Create", 0); -// _extHelper.waitForExtDialog("Define Participant Group"); -// setFormElement("groupLabel", "Category1"); -// setFormElement("categoryIdentifiers", PTIDS[0] + "," + PTIDS[1]); -// _extHelper.clickExtButton("Define Participant Group", "Save", 0); - } - private void checkTubeType() { // Field check for Tube Type column (including conflict) From 8ba0763acafb3a8cff835f8f5480dce288076e2f Mon Sep 17 00:00:00 2001 From: Trey Chadick Date: Wed, 7 Nov 2012 19:43:54 +0000 Subject: [PATCH 066/494] Add javadoc for more deprecated helpers Remove deprecated assert methods; refactor usages to junit.Assert methods SVN r22896 |2012-11-07 19:43:54 +0000 --- src/org/labkey/test/tests/SpecimenTest.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/org/labkey/test/tests/SpecimenTest.java b/src/org/labkey/test/tests/SpecimenTest.java index 3b63aa3e..dfc70317 100644 --- a/src/org/labkey/test/tests/SpecimenTest.java +++ b/src/org/labkey/test/tests/SpecimenTest.java @@ -467,7 +467,7 @@ private void verifyNotificationEmails() clickLinkContainingText("Specimen Request Notification", 1, false); assertTextPresent(_specimen_KCMC); DataRegionTable mailTable = new DataRegionTable("EmailRecord", this, false, false); - assertEquals("Notification was not as expected", notification, mailTable.getDataAsText(1, "Message")); + Assert.assertEquals("Notification was not as expected", notification, mailTable.getDataAsText(1, "Message")); } else { @@ -477,7 +477,7 @@ private void verifyNotificationEmails() clickLinkContainingText("Specimen Request Notification", 1, false); assertTextPresent(_specimen_McMichael); DataRegionTable mailTable = new DataRegionTable("EmailRecord", this, false, false); - assertEquals("Notification was not as expected", notification, mailTable.getDataAsText(0, "Message")); + Assert.assertEquals("Notification was not as expected", notification, mailTable.getDataAsText(0, "Message")); } String attachment1 = getAttribute(Locator.linkWithText(ATTACHMENT1), "href"); @@ -485,8 +485,8 @@ private void verifyNotificationEmails() try { - assertEquals("Bad link to attachment: " + ATTACHMENT1, HttpStatus.SC_OK, WebTestHelper.getHttpGetResponse(attachment1)); - assertEquals("Bad link to attachment: " + String.format(ATTACHMENT2, date), HttpStatus.SC_OK, WebTestHelper.getHttpGetResponse(attachment2)); + Assert.assertEquals("Bad link to attachment: " + ATTACHMENT1, HttpStatus.SC_OK, WebTestHelper.getHttpGetResponse(attachment1)); + Assert.assertEquals("Bad link to attachment: " + String.format(ATTACHMENT2, date), HttpStatus.SC_OK, WebTestHelper.getHttpGetResponse(attachment2)); } catch (HttpException e) { From 3a68d3e4ac0e9228c42416308176cf067111e1d1 Mon Sep 17 00:00:00 2001 From: Trey Chadick Date: Wed, 28 Nov 2012 17:53:56 +0000 Subject: [PATCH 067/494] Compensate for slow loading of study overview SVN r23328 |2012-11-28 17:53:56 +0000 --- src/org/labkey/test/tests/SpecimenTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/org/labkey/test/tests/SpecimenTest.java b/src/org/labkey/test/tests/SpecimenTest.java index dfc70317..d677423f 100644 --- a/src/org/labkey/test/tests/SpecimenTest.java +++ b/src/org/labkey/test/tests/SpecimenTest.java @@ -122,10 +122,10 @@ private void setupRequestabilityRules() waitForText("Saved", WAIT_FOR_JAVASCRIPT); clickLinkWithText(getStudyLabel()); - clickLinkWithText("Manage Study"); - clickLinkWithText("Manage Requestability Rules"); + waitAndClick(Locator.linkWithText("Manage Study")); + waitAndClick(Locator.linkWithText("Manage Requestability Rules")); // Verify that LOCKED_IN_REQUEST is the last rule - assertElementPresent(Locator.xpath("//div[contains(@class, 'x-grid3-row-last')]//div[text()='Locked In Request Check']")); + waitForElement(Locator.xpath("//div[contains(@class, 'x-grid3-row-last')]//div[text()='Locked In Request Check']")); mouseDown(Locator.xpath("//div[contains(@class, 'x-grid3-row-last')]//div[text()='Locked In Request Check']")); // Verify that LOCKED_IN_REQUEST rule cannot be moved or deleted assertElementPresent(Locator.xpath("//table[@id='btn_deleteEngine' and contains(@class, 'x-item-disabled')]")); From a94e129558d7c8ede0e6b04055da45512e7af083 Mon Sep 17 00:00:00 2001 From: Elizabeth Vannostrand Date: Thu, 29 Nov 2012 21:21:40 +0000 Subject: [PATCH 068/494] Ancillary Study from specimen request test SVN r23385 |2012-11-29 21:21:40 +0000 --- src/org/labkey/test/tests/SpecimenTest.java | 24 ++++++--------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/src/org/labkey/test/tests/SpecimenTest.java b/src/org/labkey/test/tests/SpecimenTest.java index d677423f..9ecb02aa 100644 --- a/src/org/labkey/test/tests/SpecimenTest.java +++ b/src/org/labkey/test/tests/SpecimenTest.java @@ -168,23 +168,6 @@ private void checkTubeType() assertTextPresent("15ml Cryovial", 3); } - private void setupSpecimenManagement() - { - clickLinkWithText(getStudyLabel()); - clickLinkWithText("Manage Study"); - clickLinkWithText("Manage Request Statuses"); - setFormElement("newLabel", "New Request"); - clickButton("Save"); - setFormElement("newLabel", "Processing"); - clickButton("Save"); - setFormElement("newLabel", "Completed"); - checkCheckbox("newFinalState"); - clickButton("Save"); - setFormElement("newLabel", "Rejected"); - checkCheckbox("newFinalState"); - uncheckCheckbox("newSpecimensLocked"); - clickButton("Done"); - } private void setupActorsAndGroups() { @@ -367,6 +350,7 @@ private void createRequest() assertTextPresent("BAA07XNP-01"); assertTextPresent("DAA07YGW-01"); + verifyCreateStudyFromVial(); // submit request assertTextPresent("Not Yet Submitted"); assertTextNotPresent("New Request"); @@ -387,6 +371,12 @@ private void createRequest() assertTextPresent("Complete"); } + private void verifyCreateStudyFromVial() + { + clickButton("Create Study", WAIT_FOR_EXT_MASK_TO_APPEAR); + clickButton("Next", WAIT_FOR_EXT_MASK_TO_APPEAR); + } + private void verifyViews() { clickLinkWithText("View History"); From 31adc9b48f5377be7d9a30935ad83095a3b6734e Mon Sep 17 00:00:00 2001 From: Karl Lum Date: Thu, 6 Dec 2012 21:56:09 +0000 Subject: [PATCH 069/494] Merge 12.3 release branch changes through 23482 SVN r23484 |2012-12-06 21:56:09 +0000 --- src/org/labkey/test/tests/SpecimenTest.java | 67 +++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/src/org/labkey/test/tests/SpecimenTest.java b/src/org/labkey/test/tests/SpecimenTest.java index 9ecb02aa..32e3ad4d 100644 --- a/src/org/labkey/test/tests/SpecimenTest.java +++ b/src/org/labkey/test/tests/SpecimenTest.java @@ -23,6 +23,7 @@ import org.labkey.test.WebTestHelper; import org.labkey.test.util.DataRegionTable; import org.labkey.test.util.ExtHelper; +import org.labkey.test.util.LogMethod; import org.labkey.test.util.PasswordUtil; import org.labkey.test.util.ext4cmp.Ext4FieldRef; @@ -98,6 +99,7 @@ protected void doVerifySteps() verifyActorDetails(); createRequest(); verifyViews(); + verifyAdditionalRequestFields(); verifyNotificationEmails(); verifyRequestCancel(); verifyReports(); @@ -410,6 +412,71 @@ private void verifyViews() clickButton("Cancel"); } + @LogMethod + private void verifyAdditionalRequestFields() + { + log("verifying addtional freezer fields from the exports"); + clickLinkWithText("Originating Location Specimen Lists"); + addUrlParameter("exportAsWebPage=true"); + refresh(); + + pushLocation(); + clickLinkContainingText("Export to text file"); + + // verify the additional columns + assertTextPresent("Freezer", "Fr Container", "Fr Position", "Fr Level1", "Fr Level2"); + popLocation(); + + // customize the locationSpecimenListTable then make sure changes are propogated to the exported lists + log("customizing the locationSpecimenList default view"); + pushLocation(); + goToSchemaBrowser(); + selectQuery("study", "LocationSpecimenList"); + waitForText("view data"); + clickLinkContainingText("view data"); + + _customizeViewsHelper.openCustomizeViewPanel(); + _customizeViewsHelper.removeCustomizeViewColumn("Freezer"); + _customizeViewsHelper.removeCustomizeViewColumn("Fr_Container"); + _customizeViewsHelper.removeCustomizeViewColumn("Fr_Position"); + _customizeViewsHelper.removeCustomizeViewColumn("Fr_Level1"); + _customizeViewsHelper.saveCustomView(); + popLocation(); + + log("verifying column changes"); + clickLinkWithText(getStudyLabel()); + clickLinkWithText("Specimen Data"); + Locator requests = Locator.xpath("//a[text() = 'View Current Requests']"); + waitForElement(requests); + clickAndWait(requests, defaultWaitForPage); + + clickButton("Details"); + clickLinkWithText("Originating Location Specimen Lists"); + addUrlParameter("exportAsWebPage=true"); + refresh(); + pushLocation(); + clickLinkContainingText("Export to text file"); + + // verify the additional columns + assertTextNotPresent("Freezer", "Fr Container", "Fr Position", "Fr Level1"); + assertTextPresent("Fr Level2"); + popLocation(); + + clickButton("Cancel"); + clickLinkWithText("Providing Location Specimen Lists"); + addUrlParameter("exportAsWebPage=true"); + refresh(); + pushLocation(); + clickLinkContainingText("Export to text file"); + + // verify the additional columns + assertTextNotPresent("Freezer", "Fr Container", "Fr Position", "Fr Level1"); + assertTextPresent("Fr Level2"); + popLocation(); + + clickButton("Cancel"); + } + private String _specimen_McMichael; private String _specimen_KCMC; private final static String ATTACHMENT1 = "KCMC_Moshi_Ta_to_Aurum_Health_.tsv"; From 3b39b5c5194c8abfa2a37b8c8df82d352e47ec57 Mon Sep 17 00:00:00 2001 From: Elizabeth Vannostrand Date: Wed, 12 Dec 2012 18:35:46 +0000 Subject: [PATCH 070/494] remove erroneous create-study-from-specimen call from specimen test SVN r23561 |2012-12-12 18:35:46 +0000 --- src/org/labkey/test/tests/SpecimenTest.java | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/org/labkey/test/tests/SpecimenTest.java b/src/org/labkey/test/tests/SpecimenTest.java index 32e3ad4d..3ad52ce1 100644 --- a/src/org/labkey/test/tests/SpecimenTest.java +++ b/src/org/labkey/test/tests/SpecimenTest.java @@ -352,7 +352,6 @@ private void createRequest() assertTextPresent("BAA07XNP-01"); assertTextPresent("DAA07YGW-01"); - verifyCreateStudyFromVial(); // submit request assertTextPresent("Not Yet Submitted"); assertTextNotPresent("New Request"); @@ -373,11 +372,6 @@ private void createRequest() assertTextPresent("Complete"); } - private void verifyCreateStudyFromVial() - { - clickButton("Create Study", WAIT_FOR_EXT_MASK_TO_APPEAR); - clickButton("Next", WAIT_FOR_EXT_MASK_TO_APPEAR); - } private void verifyViews() { From 52e5d46a945838c77fa1a116f1886e011b3c1c57 Mon Sep 17 00:00:00 2001 From: Trey Chadick Date: Wed, 26 Dec 2012 19:08:37 +0000 Subject: [PATCH 071/494] Add test for requesting location types WebDriver migration for SpecimenTest SVN r23772 |2012-12-26 19:08:37 +0000 --- src/org/labkey/test/tests/SpecimenTest.java | 440 +++++++++++++------- 1 file changed, 292 insertions(+), 148 deletions(-) diff --git a/src/org/labkey/test/tests/SpecimenTest.java b/src/org/labkey/test/tests/SpecimenTest.java index 3ad52ce1..bd30aa20 100644 --- a/src/org/labkey/test/tests/SpecimenTest.java +++ b/src/org/labkey/test/tests/SpecimenTest.java @@ -22,13 +22,13 @@ import org.labkey.test.Locator; import org.labkey.test.WebTestHelper; import org.labkey.test.util.DataRegionTable; -import org.labkey.test.util.ExtHelper; import org.labkey.test.util.LogMethod; import org.labkey.test.util.PasswordUtil; -import org.labkey.test.util.ext4cmp.Ext4FieldRef; +import org.labkey.test.util.ext4cmp.Ext4FieldRefWD; import java.io.IOException; import java.text.SimpleDateFormat; +import java.util.Arrays; import java.util.Date; import java.util.List; @@ -37,11 +37,12 @@ * Date: Mar 9, 2006 * Time: 1:54:57 PM */ -public class SpecimenTest extends StudyBaseTest +public class SpecimenTest extends StudyBaseTestWD { protected static final String PROJECT_NAME = "SpecimenVerifyProject"; public static final String SPECIMEN_DETAIL = "SpecimenDetail"; - private static final String DESTINATION_SITE = "Aurum Health KOSH Lab, Orkney, South Africa (Repository)"; + private static final String DESTINATION_SITE = "Aurum Health KOSH Lab, Orkney, South Africa (Endpoint Lab, Repository)"; + private static final String SOURCE_SITE = "Contract Lab Services, Johannesburg, South Africa (Repository, Clinic)"; private static final String USER1 = "user1@specimen.test"; private static final String USER2 = "user2@specimen.test"; private static final String REQUESTABILITY_QUERY = "RequestabilityRule"; @@ -67,7 +68,7 @@ protected boolean isFileUploadTest() return true; } - @Override + @Override @LogMethod protected void doCreateSteps() { enableEmailRecorder(); @@ -93,7 +94,7 @@ protected void doCreateSteps() uploadSpecimensFromFile(); } - @Override + @Override @LogMethod protected void doVerifySteps() { verifyActorDetails(); @@ -104,17 +105,19 @@ protected void doVerifySteps() verifyRequestCancel(); verifyReports(); exportSpecimenTest(); + verifyRequestingLocationRestriction(); searchTest(); } + @LogMethod private void setupRequestabilityRules() { // Create custom query to test requestability rules. goToSchemaBrowser(); selectQuery("study", SPECIMEN_DETAIL); clickButton("Create New Query"); - setFormElement("ff_newQueryName", REQUESTABILITY_QUERY); - clickLinkWithText("Create and Edit Source"); + setFormElement(Locator.name("ff_newQueryName"), REQUESTABILITY_QUERY); + clickAndWait(Locator.linkWithText("Create and Edit Source")); setQueryEditorValue("queryText", "SELECT \n" + SPECIMEN_DETAIL + ".GlobalUniqueId AS GlobalUniqueId\n" + @@ -123,17 +126,17 @@ private void setupRequestabilityRules() clickButton("Save", 0); waitForText("Saved", WAIT_FOR_JAVASCRIPT); - clickLinkWithText(getStudyLabel()); + clickAndWait(Locator.linkWithText(getStudyLabel())); waitAndClick(Locator.linkWithText("Manage Study")); waitAndClick(Locator.linkWithText("Manage Requestability Rules")); // Verify that LOCKED_IN_REQUEST is the last rule waitForElement(Locator.xpath("//div[contains(@class, 'x-grid3-row-last')]//div[text()='Locked In Request Check']")); - mouseDown(Locator.xpath("//div[contains(@class, 'x-grid3-row-last')]//div[text()='Locked In Request Check']")); + click(Locator.xpath("//div[contains(@class, 'x-grid3-row-last')]//div[text()='Locked In Request Check']")); // Verify that LOCKED_IN_REQUEST rule cannot be moved or deleted assertElementPresent(Locator.xpath("//table[@id='btn_deleteEngine' and contains(@class, 'x-item-disabled')]")); assertElementPresent(Locator.xpath("//table[@id='btn_moveUp' and contains(@class, 'x-item-disabled')]")); assertElementPresent(Locator.xpath("//table[@id='btn_moveDown' and contains(@class, 'x-item-disabled')]")); - mouseDown(Locator.xpath("//div[contains(@class, 'x-grid3-col-numberer') and text()='2']")); + click(Locator.xpath("//div[contains(@class, 'x-grid3-col-numberer') and text()='2']")); assertElementPresent(Locator.xpath("//table[@id='btn_deleteEngine' and not(contains(@class, 'x-item-disabled'))]")); assertElementPresent(Locator.xpath("//table[@id='btn_moveUp' and not(contains(@class, 'x-item-disabled'))]")); assertElementPresent(Locator.xpath("//table[@id='btn_moveDown' and contains(@class, 'x-item-disabled')]")); @@ -146,110 +149,117 @@ private void setupRequestabilityRules() clickButton("Save"); } + @LogMethod private void checkTubeType() { // Field check for Tube Type column (including conflict) - clickLinkWithText(getStudyLabel()); + clickAndWait(Locator.linkWithText(getStudyLabel())); addWebPart("Specimens"); - clickLinkWithText("By Individual Vial"); + clickAndWait(Locator.linkWithText("By Individual Vial")); setFilter(SPECIMEN_DETAIL, "PrimaryType", "Is Blank"); // Verify that there's only one vial of unknown type: assertLinkPresentWithTextCount("[history]", 1); // There's a conflict in TubeType for this vial's events; verify that no TubeType is populated at the vial level assertTextNotPresent("Cryovial"); - clickLinkWithText("[history]"); + clickAndWait(Locator.linkWithText("[history]")); // This vial has three events, each of which list a different tube type: assertTextPresent("15ml Cryovial"); assertTextPresent("20ml Cryovial"); assertTextPresent("25ml Cryovial"); - clickLinkWithText("Specimen Overview"); - clickLinkWithText("Vials by Derivative", false); + clickAndWait(Locator.linkWithText("Specimen Overview")); + click(Locator.linkWithText("Vials by Derivative")); waitAndClick(WAIT_FOR_JAVASCRIPT, Locator.linkWithText("Tear Flo Strips"), WAIT_FOR_PAGE); // For these three vials, there should be no conflict in TubeType, so we should see the text once for each of three vials: assertLinkPresentWithTextCount("[history]", 3); assertTextPresent("15ml Cryovial", 3); } - + @LogMethod private void setupActorsAndGroups() { - clickLinkWithText("Manage Actors and Groups"); - setFormElement("newLabel", "SLG"); - selectOptionByText("newPerSite", "One Per Study"); + clickAndWait(Locator.linkWithText("Manage Actors and Groups")); + setFormElement(Locator.name("newLabel"), "SLG"); + selectOptionByText(Locator.name("newPerSite"), "One Per Study"); clickButton("Save"); - clickLinkWithText("Update Members"); - setText("names", USER1); + clickAndWait(Locator.linkWithText("Update Members")); + setFormElement(Locator.name("names"), USER1); uncheckCheckbox("sendEmail"); clickButton("Update Members"); - setFormElement("newLabel", "IRB"); - selectOptionByText("newPerSite", "Multiple Per Study (Location Affiliated)"); + setFormElement(Locator.name("newLabel"), "IRB"); + selectOptionByText(Locator.name("newPerSite"), "Multiple Per Study (Location Affiliated)"); clickButton("Save"); - clickLinkWithText("Update Members", 1); - clickLinkWithText(DESTINATION_SITE); - setText("names", USER2); + clickAndWait(Locator.linkWithText("Update Members").index(1)); + clickAndWait(Locator.linkWithText(DESTINATION_SITE)); + setFormElement(Locator.name("names"), USER2); uncheckCheckbox("sendEmail"); - clickLinkWithText("Update Members"); - clickLinkWithText(getStudyLabel()); + clickAndWait(Locator.linkWithText("Update Members")); + clickAndWait(Locator.linkWithText(getStudyLabel())); } + @LogMethod (quiet = true) private void setupDefaultRequirements() { - clickLinkWithText("Manage Study"); - clickLinkWithText("Manage Default Requirements"); - selectOptionByText("originatorActor", "IRB"); - setFormElement("originatorDescription", "Originating IRB Approval"); + clickAndWait(Locator.linkWithText("Manage Study")); + clickAndWait(Locator.linkWithText("Manage Default Requirements")); + selectOptionByText(Locator.name("originatorActor"), "IRB"); + setFormElement(Locator.name("originatorDescription"), "Originating IRB Approval"); clickButton("Add Requirement"); - selectOptionByText("providerActor", "IRB"); - setFormElement("providerDescription", "Providing IRB Approval"); - clickLink(Locator.xpath("//input[@name='providerDescription']/../.." + Locator.navButton("Add Requirement").getPath())); - selectOptionByText("receiverActor", "IRB"); - setFormElement("receiverDescription", "Receiving IRB Approval"); - clickLink(Locator.xpath("//input[@name='receiverDescription']/../.." + Locator.navButton("Add Requirement").getPath())); - selectOptionByText("generalActor", "SLG"); - setFormElement("generalDescription", "SLG Approval"); - clickLink(Locator.xpath("//input[@name='generalDescription']/../.." + Locator.navButton("Add Requirement").getPath())); + selectOptionByText(Locator.name("providerActor"), "IRB"); + setFormElement(Locator.name("providerDescription"), "Providing IRB Approval"); + clickAndWait(Locator.xpath("//input[@name='providerDescription']/../.." + Locator.navButton("Add Requirement").getPath())); + selectOptionByText(Locator.name("receiverActor"), "IRB"); + setFormElement(Locator.name("receiverDescription"), "Receiving IRB Approval"); + clickAndWait(Locator.xpath("//input[@name='receiverDescription']/../.." + Locator.navButton("Add Requirement").getPath())); + selectOptionByText(Locator.name("generalActor"), "SLG"); + setFormElement(Locator.name("generalDescription"), "SLG Approval"); + clickAndWait(Locator.xpath("//input[@name='generalDescription']/../.." + Locator.navButton("Add Requirement").getPath())); clickTab("Manage"); } + @LogMethod (quiet = true) private void setupRequestForm() { - clickLinkWithText("Manage New Request Form"); + clickAndWait(Locator.linkWithText("Manage New Request Form")); clickButton("Add New Input", 0); - setFormElement("//descendant::input[@name='title'][4]", "Last One"); - setFormElement("//descendant::input[@name='helpText'][4]", "A test input"); + setFormElement(Locator.xpath("//descendant::input[@name='title'][4]"), "Last One"); + setFormElement(Locator.xpath("//descendant::input[@name='helpText'][4]"), "A test input"); click(Locator.xpath("//descendant::input[@name='required'][4]")); clickButton("Save"); - clickLinkWithText(getStudyLabel()); + clickAndWait(Locator.linkWithText(getStudyLabel())); } + @LogMethod private void setupActorNotification() { log("Check Configure Defaults for Actor Notification"); - clickLinkWithText(getStudyLabel()); - clickLinkWithText("Manage"); - clickLinkWithText("Manage Notifications"); + clickAndWait(Locator.linkWithText(getStudyLabel())); + clickAndWait(Locator.linkWithText("Manage")); + clickAndWait(Locator.linkWithText("Manage Notifications")); assertTextPresent("Default Email Recipients"); checkRadioButton("defaultEmailNotify", "All"); clickButton("Save"); } + @LogMethod private void uploadSpecimensFromFile() { log("Check Upload Specimen List from file"); - clickLinkWithText(getStudyLabel()); - clickLinkWithText("Specimen Data"); - clickLinkWithText("Create New Request"); - selectOptionByText("destinationSite", "Aurum Health KOSH Lab, Orkney, South Africa (Repository)"); - setFormElement("input0", "Assay Plan"); - setFormElement("input2", "Comments"); - setFormElement("input1", "Shipping"); - setFormElement("input3", "Last one"); + clickAndWait(Locator.linkWithText(getStudyLabel())); + clickAndWait(Locator.linkWithText("Specimen Data")); + waitAndClick(Locator.linkWithText("Specimen Requests")); // expand node in Specimens webpart + clickAndWait(Locator.linkWithText("Create New Request")); + selectOptionByText(Locator.name("destinationSite"), DESTINATION_SITE); + setFormElement(Locator.id("input0"), "Assay Plan"); + setFormElement(Locator.id("input2"), "Comments"); + setFormElement(Locator.id("input1"), "Shipping"); + setFormElement(Locator.id("input3"), "Last one"); clickButton("Create and View Details"); - clickLinkWithText("Upload Specimen Ids"); + clickAndWait(Locator.linkWithText("Upload Specimen Ids")); setFormElement(Locator.xpath("//textarea[@id='tsv3']"), "AAA07XK5-01"); // add specimen clickButton("Submit"); // Submit button - clickLinkWithText("Upload Specimen Ids"); + waitAndClick(Locator.linkWithText("Upload Specimen Ids")); + waitForElement(Locator.xpath("//textarea[@id='tsv3']")); setFormElement(Locator.xpath("//textarea[@id='tsv3']"), "AAA07XK5-01"); // try to add again clickButton("Submit", 0); // Submit button waitForText("Specimen AAA07XK5-01 not available", 20000); @@ -260,6 +270,7 @@ private void uploadSpecimensFromFile() clickButton("Submit"); // Submit button } + @LogMethod (quiet = true) private void verifyActorDetails() { // Check each Actor's Details for "Default Actor Notification" feature; @@ -267,7 +278,7 @@ private void verifyActorDetails() List detailsLinks = findAllMatches(Locator.xpath("//td[a='Details']/a")); for (Locator link : detailsLinks) { - clickLink(link); + clickAndWait(link); List allCheckBoxes = findAllMatches(Locator.xpath("//input[@type='checkbox' and @name='notificationIdPairs']")); List checkedCheckBoxes = findAllMatches(Locator.xpath("//input[@type='checkbox' and @name='notificationIdPairs' and @checked]")); List disabledCheckBoxes = findAllMatches(Locator.xpath("//input[@type='checkbox' and @name='notificationIdPairs' and @disabled]")); @@ -275,19 +286,21 @@ private void verifyActorDetails() clickButton("Cancel"); } - assertTextPresent("Associated Specimens"); + waitForElement(Locator.css("span.labkey-wp-title-text").withText("Associated Specimens")); assertTextPresent("AAA07XK5-01", "AAA07XK5-04", "AAA07XK5-06", "AAA07XSF-03"); - clickButton("Cancel Request"); - Assert.assertTrue(getConfirmationAndWait().matches("^Canceling will permanently delete this pending request\\. Continue[\\s\\S]$")); + clickButton("Cancel Request", 0); + assertAlert("Canceling will permanently delete this pending request. Continue?"); + waitForElement(Locator.id("dataregion_SpecimenRequest")); } + @LogMethod private void createRequest() { - clickLinkWithText(getStudyLabel()); - clickLinkWithText("Specimen Data"); - clickLinkWithText("Vials by Derivative", false); + clickAndWait(Locator.linkWithText(getStudyLabel())); + clickAndWait(Locator.linkWithText("Specimen Data")); + click(Locator.linkWithText("Vials by Derivative")); waitAndClick(WAIT_FOR_JAVASCRIPT, Locator.linkWithText("Plasma, Unknown Processing"), WAIT_FOR_PAGE); // Verify unavailable sample assertElementPresent(Locator.xpath("//input[@id='check_" + UNREQUESTABLE_SAMPLE + "' and @disabled]")); @@ -296,24 +309,24 @@ private void createRequest() checkCheckbox(".toggle"); clickMenuButton("Page Size", "Show All"); - clickLinkContainingText("history"); + clickAndWait(Locator.linkContainingText("history")); assertTextPresent("Vial History"); goBack(); clickMenuButton("Request Options", "Create New Request"); - selectOptionByText("destinationSite", "Aurum Health KOSH Lab, Orkney, South Africa (Repository)"); - setFormElement("input0", "Assay Plan"); - setFormElement("input2", "Comments"); - setFormElement("input1", "Shipping"); + selectOptionByText(Locator.name("destinationSite"), DESTINATION_SITE); + setFormElement(Locator.id("input0"), "Assay Plan"); + setFormElement(Locator.id("input2"), "Comments"); + setFormElement(Locator.id("input1"), "Shipping"); clickButton("Create and View Details"); assertTextPresent("Please provide all required input."); - setFormElement("input3", "sample last one input"); + setFormElement(Locator.id("input3"), "sample last one input"); clickButton("Create and View Details"); assertTextPresent("sample last one input"); assertTextPresent("IRB"); assertTextPresent("KCMC, Moshi, Tanzania"); assertTextPresent("Originating IRB Approval"); - assertTextPresent("Contract Lab Services, Johannesburg, South Africa (Repository)"); + assertTextPresent(SOURCE_SITE); assertTextPresent("Providing IRB Approval"); assertTextPresent(DESTINATION_SITE); assertTextPresent("Receiving IRB Approval"); @@ -324,11 +337,11 @@ private void createRequest() // verify that the swab specimen isn't present yet assertTextNotPresent("DAA07YGW-01"); assertTextNotPresent("Complete"); - clickLinkWithText(getStudyLabel()); + clickAndWait(Locator.linkWithText(getStudyLabel())); // add additional specimens - clickLinkWithText("Specimen Data"); - clickLinkWithText("Vials by Derivative", false); + clickAndWait(Locator.linkWithText("Specimen Data")); + waitAndClick(Locator.linkWithText("Vials by Derivative")); waitAndClick(WAIT_FOR_JAVASCRIPT, Locator.linkWithText("Swab"), WAIT_FOR_PAGE); checkCheckbox(".toggle"); clickMenuButtonAndContinue("Request Options", "Add To Existing Request"); @@ -343,7 +356,7 @@ private void createRequest() assertTextPresent("IRB"); assertTextPresent("KCMC, Moshi, Tanzania"); assertTextPresent("Originating IRB Approval"); - assertTextPresent("Contract Lab Services, Johannesburg, South Africa (Repository)"); + assertTextPresent(SOURCE_SITE); assertTextPresent("Providing IRB Approval"); assertTextPresent(DESTINATION_SITE); assertTextPresent("Receiving IRB Approval"); @@ -361,10 +374,10 @@ private void createRequest() assertTextPresent("New Request"); // modify request - selectOptionByText("newActor", "SLG"); - setFormElement("newDescription", "Other SLG Approval"); + selectOptionByText(Locator.name("newActor"), "SLG"); + setFormElement(Locator.name("newDescription"), "Other SLG Approval"); clickButton("Add Requirement"); - clickLinkWithText("Details"); + clickAndWait(Locator.linkWithText("Details")); checkCheckbox("complete"); checkCheckbox("notificationIdPairs"); checkCheckbox("notificationIdPairs", 1); @@ -372,16 +385,16 @@ private void createRequest() assertTextPresent("Complete"); } - + @LogMethod private void verifyViews() { - clickLinkWithText("View History"); + clickAndWait(Locator.linkWithText("View History")); assertTextPresent("Request submitted for processing."); assertTextPresent("Notification Sent", 2); assertTextPresent(USER1); assertTextPresent(USER2); - clickLinkWithText("View Request"); - clickLinkWithText("Originating Location Specimen Lists"); + clickAndWait(Locator.linkWithText("View Request")); + clickAndWait(Locator.linkWithText("Originating Location Specimen Lists")); // Ordering of locations is nondeterministic if (isPresentInThisOrder("The McMichael Lab, Oxford, UK", "KCMC, Moshi, Tanzania") == null) { @@ -401,8 +414,8 @@ private void verifyViews() checkCheckbox("sendTsv"); clickButton("Send Email"); _requestId = Integer.parseInt(getUrlParam(getURL().toString(), "id", false)); - clickLinkWithText("Providing Location Specimen Lists"); - assertTextPresent("Contract Lab Services, Johannesburg, South Africa (Repository)"); + clickAndWait(Locator.linkWithText("Providing Location Specimen Lists")); + assertTextPresent(SOURCE_SITE); clickButton("Cancel"); } @@ -410,12 +423,12 @@ private void verifyViews() private void verifyAdditionalRequestFields() { log("verifying addtional freezer fields from the exports"); - clickLinkWithText("Originating Location Specimen Lists"); + clickAndWait(Locator.linkWithText("Originating Location Specimen Lists")); addUrlParameter("exportAsWebPage=true"); refresh(); pushLocation(); - clickLinkContainingText("Export to text file"); + clickAndWait(Locator.linkContainingText("Export to text file")); // verify the additional columns assertTextPresent("Freezer", "Fr Container", "Fr Position", "Fr Level1", "Fr Level2"); @@ -427,7 +440,7 @@ private void verifyAdditionalRequestFields() goToSchemaBrowser(); selectQuery("study", "LocationSpecimenList"); waitForText("view data"); - clickLinkContainingText("view data"); + clickAndWait(Locator.linkContainingText("view data")); _customizeViewsHelper.openCustomizeViewPanel(); _customizeViewsHelper.removeCustomizeViewColumn("Freezer"); @@ -438,18 +451,17 @@ private void verifyAdditionalRequestFields() popLocation(); log("verifying column changes"); - clickLinkWithText(getStudyLabel()); - clickLinkWithText("Specimen Data"); - Locator requests = Locator.xpath("//a[text() = 'View Current Requests']"); - waitForElement(requests); - clickAndWait(requests, defaultWaitForPage); + clickAndWait(Locator.linkWithText(getStudyLabel())); + clickAndWait(Locator.linkWithText("Specimen Data")); + waitAndClick(Locator.linkWithText("Specimen Requests")); // expand node in Specimens webpart + clickAndWait(Locator.xpath("//a[text() = 'View Current Requests']")); clickButton("Details"); - clickLinkWithText("Originating Location Specimen Lists"); + clickAndWait(Locator.linkWithText("Originating Location Specimen Lists")); addUrlParameter("exportAsWebPage=true"); refresh(); pushLocation(); - clickLinkContainingText("Export to text file"); + clickAndWait(Locator.linkContainingText("Export to text file")); // verify the additional columns assertTextNotPresent("Freezer", "Fr Container", "Fr Position", "Fr Level1"); @@ -457,11 +469,11 @@ private void verifyAdditionalRequestFields() popLocation(); clickButton("Cancel"); - clickLinkWithText("Providing Location Specimen Lists"); + clickAndWait(Locator.linkWithText("Providing Location Specimen Lists")); addUrlParameter("exportAsWebPage=true"); refresh(); pushLocation(); - clickLinkContainingText("Export to text file"); + clickAndWait(Locator.linkContainingText("Export to text file")); // verify the additional columns assertTextNotPresent("Freezer", "Fr Container", "Fr Position", "Fr Level1"); @@ -476,34 +488,52 @@ private void verifyAdditionalRequestFields() private final static String ATTACHMENT1 = "KCMC_Moshi_Ta_to_Aurum_Health_.tsv"; private final static String ATTACHMENT2 = "KCMC_Moshi_Ta_to_Aurum_Health_KO_%s.xls"; // Params: date(yyyy-MM-dd) private final String NOTIFICATION_TEMPLATE = // Params: Study Name, requestId, Study Name, requestId, Username, Date(yyyy-MM-dd) - "%s: Specimen Request Notification \n" + - " \n" + - " Specimen request #%s was updated in %s. \n" + - " \n" + - " \n" + - " Request Details Specimen Request %s Destination Aurum Health KOSH Lab, Orkney, South Africa (Repository) Status New Request Modified by %s Action Originating location notification of specimen shipment to Aurum Health KOSH Lab, Orkney, South Africa (Repository) Attachments KCMC_Moshi_Ta_to_Aurum_Health_.tsv \n" + - " KCMC_Moshi_Ta_to_Aurum_Health_KO_%s.xls \n" + - " Assay Plan:\n" + - " Assay Plan\n" + - " \n" + - " Shipping Information:\n" + - " Shipping\n" + - " \n" + - " Comments:\n" + - " Comments\n" + - " \n" + - " Last One:\n" + - " sample last one input \n" + - " Specimen List (Request Link)\n" + + "%s: Specimen Request Notification\n" + + "\n" + + "\n" + + "Specimen request #%s was updated in %s.\n" + + "\n" + + "Request Details\n" + + "Specimen Request %s\n" + + "Destination " + DESTINATION_SITE+"\n" + + "Status New Request\n" + + "Modified by %s\n" + + "Action Originating location notification of specimen shipment to "+DESTINATION_SITE+"\n" + + "Attachments KCMC_Moshi_Ta_to_Aurum_Health_.tsv\n" + + "KCMC_Moshi_Ta_to_Aurum_Health_KO_%s.xls\n" + + "Assay Plan:\n" + + "Assay Plan\n" + + "\n" + + "Shipping Information:\n" + + "Shipping\n" + "\n" + - " \n" + - " Participant Id Global Unique Id Visit Description Visit Volume Volume Units Primary Type Derivative Type Additive Type Derivative Type2 Sub Additive Derivative Draw Timestamp Clinic Processing Location First Processed By Initials Sal Receipt Date Class Id Protocol Number Primary Volume Primary Volume Units Total Cell Count Tube Type Comments Locked In Request Requestable Site Name Site Ldms Code At Repository Available Availability Reason Quality Control Flag Quality Control Comments Collection Cohort Vial Count Locked In Request Count At Repository Count Available Count Expected Available Count 1 999320824 BAA07XNP-01 Vst 1.0 ML Blood (Whole) Plasma, Unknown Processing EDTA N/A 2005-12-23 10:05:00.0 KCMC, Moshi, Tanzania Contract Lab Services, Johannesburg, South Africa LK 2005-12-23 00:00:00.0 LABK 39 15ml Cryovial true Contract Lab Services, Johannesburg, South Africa 350 true false This vial is unavailable because it is locked in a specimen request. false 2 1 2 0 1 2 999320087 CAA07XN8-01 Vst 1.0 ML Vaginal Swab Swab None N/A 2005-12-22 12:50:00.0 KCMC, Moshi, Tanzania Contract Lab Services, Johannesburg, South Africa LK 2005-12-22 00:00:00.0 LABK 39 15ml Cryovial true Contract Lab Services, Johannesburg, South Africa 350 true false This vial is unavailable because it is locked in a specimen request. false 1 1 1 0 0 3 999320706 DAA07YGW-01 Vst 1.0 ML Vaginal Swab Swab None N/A 2006-01-05 10:00:00.0 KCMC, Moshi, Tanzania Contract Lab Services, Johannesburg, South Africa LK 2006-01-05 00:00:00.0 LABK 39 15ml Cryovial true Contract Lab Services, Johannesburg, South Africa 350 true false This vial is unavailable because it is locked in a specimen request. false 1 1 1 0 0 4 999320898 FAA07XLJ-01 Vst 1.0 ML Vaginal Swab Swab None N/A 2005-12-20 12:05:00.0 KCMC, Moshi, Tanzania Contract Lab Services, Johannesburg, South Africa LK 2005-12-20 00:00:00.0 LABK 39 15ml Cryovial true Contract Lab Services, Johannesburg, South Africa 350 true false This vial is unavailable because it is locked in a specimen request. false 1 1 1 0 0 5 999320264 FAA07YSC-01 Vst 1.0 ML Vaginal Swab Swab None N/A 2006-01-13 12:10:00.0 KCMC, Moshi, Tanzania Contract Lab Services, Johannesburg, South Africa LK 2006-01-13 00:00:00.0 LABK 39 15ml Cryovial true Contract Lab Services, Johannesburg, South Africa 350 true false This vial is unavailable because it is locked in a specimen request. false 1 1 1 0 0 6 999320520 FAA07YXY-01 Vst 1.0 ML Vaginal Swab Swab None N/A 2005-12-15 10:30:00.0 KCMC, Moshi, Tanzania Contract Lab Services, Johannesburg, South Africa LK 2006-01-15 00:00:00.0 LABK 39 15ml Cryovial true Contract Lab Services, Johannesburg, South Africa 350 true false This vial is unavailable because it is locked in a specimen request. false 1 1 1 0 0 7 999320498 JAA07YJB-01 Vst 1.0 ML Vaginal Swab Swab None N/A 2006-01-05 09:30:00.0 KCMC, Moshi, Tanzania Contract Lab Services, Johannesburg, South Africa LK 2006-01-11 00:00:00.0 LABK 39 15ml Cryovial true Contract Lab Services, Johannesburg, South Africa 350 true false This vial is unavailable because it is locked in a specimen request. false 1 1 1 0 0 8 999320476 JAA07YSQ-01 Vst 1.0 ML Vaginal Swab Swab None N/A 2006-01-11 10:20:00.0 KCMC, Moshi, Tanzania Contract Lab Services, Johannesburg, South Africa LK 2006-01-11 00:00:00.0 LABK 39 15ml Cryovial true Contract Lab Services, Johannesburg, South Africa 350 true false This vial is unavailable because it is locked in a specimen request. false 1 1 1 0 0 9 999320980 KAA07YV1-01 Vst 1.0 ML Vaginal Swab Swab None N/A 2006-01-17 08:30:00.0 KCMC, Moshi, Tanzania Contract Lab Services, Johannesburg, South Africa LK 2006-01-17 00:00:00.0 LABK 39 15ml Cryovial true Contract Lab Services, Johannesburg, South Africa 350 true false This vial is unavailable because it is locked in a specimen request. false 1 1 1 0 0 10 999320520 KAA07YY0-01 Vst 1.0 ML Blood (Whole) Plasma, Unknown Processing EDTA N/A 2005-12-15 10:30:00.0 KCMC, Moshi, Tanzania Contract Lab Services, Johannesburg, South Africa LK 2006-01-15 00:00:00.0 LABK 39 15ml Cryovial true Contract Lab Services, Johannesburg, South Africa 350 true false This vial is unavailable because it is locked in a specimen request. false 2 2 2 0 0 11 999320520 KAA07YY0-02 Vst 1.0 ML Blood (Whole) Plasma, Unknown Processing EDTA N/A 2005-12-15 10:30:00.0 KCMC, Moshi, Tanzania Contract Lab Services, Johannesburg, South Africa LK 2006-01-15 00:00:00.0 LABK 39 15ml Cryovial true Contract Lab Services, Johannesburg, South Africa 350 true false This vial is unavailable because it is locked in a specimen request. false 2 2 2 0 0"; + "Comments:\n" + + "Comments\n" + + "\n" + + "Last One:\n" + + "sample last one input\n" + + "Specimen List (Request Link)\n" + + "\n" + + " Participant Id Global Unique Id Visit Description Visit Volume Volume Units Primary Type Derivative Type Additive Type Derivative Type2 Sub Additive Derivative Draw Timestamp Clinic Processing Location First Processed By Initials Sal Receipt Date Class Id Protocol Number Primary Volume Primary Volume Units Total Cell Count Tube Type Comments Locked In Request Requestable Site Name Site Ldms Code At Repository Available Availability Reason Quality Control Flag Quality Control Comments Collection Cohort Vial Count Locked In Request Count At Repository Count Available Count Expected Available Count\n" + + "1 999320824 BAA07XNP-01 Vst 1.0 ML Blood (Whole) Plasma, Unknown Processing EDTA N/A 2005-12-23 10:05:00.0 KCMC, Moshi, Tanzania Contract Lab Services, Johannesburg, South Africa LK 2005-12-23 00:00:00.0 LABK 39 15ml Cryovial true Contract Lab Services, Johannesburg, South Africa 350 true false This vial is unavailable because it is locked in a specimen request. false 2 1 2 0 1\n" + + "2 999320087 CAA07XN8-01 Vst 1.0 ML Vaginal Swab Swab None N/A 2005-12-22 12:50:00.0 KCMC, Moshi, Tanzania Contract Lab Services, Johannesburg, South Africa LK 2005-12-22 00:00:00.0 LABK 39 15ml Cryovial true Contract Lab Services, Johannesburg, South Africa 350 true false This vial is unavailable because it is locked in a specimen request. false 1 1 1 0 0\n" + + "3 999320706 DAA07YGW-01 Vst 1.0 ML Vaginal Swab Swab None N/A 2006-01-05 10:00:00.0 KCMC, Moshi, Tanzania Contract Lab Services, Johannesburg, South Africa LK 2006-01-05 00:00:00.0 LABK 39 15ml Cryovial true Contract Lab Services, Johannesburg, South Africa 350 true false This vial is unavailable because it is locked in a specimen request. false 1 1 1 0 0\n" + + "4 999320898 FAA07XLJ-01 Vst 1.0 ML Vaginal Swab Swab None N/A 2005-12-20 12:05:00.0 KCMC, Moshi, Tanzania Contract Lab Services, Johannesburg, South Africa LK 2005-12-20 00:00:00.0 LABK 39 15ml Cryovial true Contract Lab Services, Johannesburg, South Africa 350 true false This vial is unavailable because it is locked in a specimen request. false 1 1 1 0 0\n" + + "5 999320264 FAA07YSC-01 Vst 1.0 ML Vaginal Swab Swab None N/A 2006-01-13 12:10:00.0 KCMC, Moshi, Tanzania Contract Lab Services, Johannesburg, South Africa LK 2006-01-13 00:00:00.0 LABK 39 15ml Cryovial true Contract Lab Services, Johannesburg, South Africa 350 true false This vial is unavailable because it is locked in a specimen request. false 1 1 1 0 0\n" + + "6 999320520 FAA07YXY-01 Vst 1.0 ML Vaginal Swab Swab None N/A 2005-12-15 10:30:00.0 KCMC, Moshi, Tanzania Contract Lab Services, Johannesburg, South Africa LK 2006-01-15 00:00:00.0 LABK 39 15ml Cryovial true Contract Lab Services, Johannesburg, South Africa 350 true false This vial is unavailable because it is locked in a specimen request. false 1 1 1 0 0\n" + + "7 999320498 JAA07YJB-01 Vst 1.0 ML Vaginal Swab Swab None N/A 2006-01-05 09:30:00.0 KCMC, Moshi, Tanzania Contract Lab Services, Johannesburg, South Africa LK 2006-01-11 00:00:00.0 LABK 39 15ml Cryovial true Contract Lab Services, Johannesburg, South Africa 350 true false This vial is unavailable because it is locked in a specimen request. false 1 1 1 0 0\n" + + "8 999320476 JAA07YSQ-01 Vst 1.0 ML Vaginal Swab Swab None N/A 2006-01-11 10:20:00.0 KCMC, Moshi, Tanzania Contract Lab Services, Johannesburg, South Africa LK 2006-01-11 00:00:00.0 LABK 39 15ml Cryovial true Contract Lab Services, Johannesburg, South Africa 350 true false This vial is unavailable because it is locked in a specimen request. false 1 1 1 0 0\n" + + "9 999320980 KAA07YV1-01 Vst 1.0 ML Vaginal Swab Swab None N/A 2006-01-17 08:30:00.0 KCMC, Moshi, Tanzania Contract Lab Services, Johannesburg, South Africa LK 2006-01-17 00:00:00.0 LABK 39 15ml Cryovial true Contract Lab Services, Johannesburg, South Africa 350 true false This vial is unavailable because it is locked in a specimen request. false 1 1 1 0 0\n" + + "10 999320520 KAA07YY0-01 Vst 1.0 ML Blood (Whole) Plasma, Unknown Processing EDTA N/A 2005-12-15 10:30:00.0 KCMC, Moshi, Tanzania Contract Lab Services, Johannesburg, South Africa LK 2006-01-15 00:00:00.0 LABK 39 15ml Cryovial true Contract Lab Services, Johannesburg, South Africa 350 true false This vial is unavailable because it is locked in a specimen request. false 2 2 2 0 0\n" + + "11 999320520 KAA07YY0-02 Vst 1.0 ML Blood (Whole) Plasma, Unknown Processing EDTA N/A 2005-12-15 10:30:00.0 KCMC, Moshi, Tanzania Contract Lab Services, Johannesburg, South Africa LK 2006-01-15 00:00:00.0 LABK 39 15ml Cryovial true Contract Lab Services, Johannesburg, South Africa 350 true false This vial is unavailable because it is locked in a specimen request. false 2 2 2 0 0"; + @LogMethod private void verifyNotificationEmails() { String date = new SimpleDateFormat("yyyy-MM-dd").format(new Date()); String notification = String.format(NOTIFICATION_TEMPLATE, getStudyLabel(), _requestId, getStudyLabel(), _requestId, PasswordUtil.getUsername(), date); log("Check notification emails"); + goToHome(); goToModule("Dumbster"); assertTextPresent("Specimen Request Notification", 8); assertTextPresent(USER1, 4); @@ -512,21 +542,25 @@ private void verifyNotificationEmails() log("Check for correct data in notification emails"); if (getTableCellText("dataregion_EmailRecord", 2, 0).equals(USER1)) { - clickLinkContainingText("Specimen Request Notification", false); - assertTextPresent(_specimen_McMichael); - assertTextNotPresent(_specimen_KCMC); - clickLinkContainingText("Specimen Request Notification", 1, false); - assertTextPresent(_specimen_KCMC); + click(Locator.linkContainingText("Specimen Request Notification")); + String bodyText = getBodyText(); + Assert.assertTrue(bodyText.contains(_specimen_McMichael)); + Assert.assertTrue(!bodyText.contains(_specimen_KCMC)); + click(Locator.linkContainingText("Specimen Request Notification").index(1)); + bodyText = getBodyText(); + Assert.assertTrue(bodyText.contains(_specimen_KCMC)); DataRegionTable mailTable = new DataRegionTable("EmailRecord", this, false, false); Assert.assertEquals("Notification was not as expected", notification, mailTable.getDataAsText(1, "Message")); } else { - clickLinkContainingText("Specimen Request Notification", false); - assertTextPresent(_specimen_KCMC); - assertTextNotPresent(_specimen_McMichael); - clickLinkContainingText("Specimen Request Notification", 1, false); - assertTextPresent(_specimen_McMichael); + click(Locator.linkContainingText("Specimen Request Notification")); + String bodyText = getBodyText(); + Assert.assertTrue(bodyText.contains(_specimen_KCMC)); + Assert.assertTrue(!bodyText.contains(_specimen_McMichael)); + click(Locator.linkContainingText("Specimen Request Notification").index(1)); + bodyText = getBodyText(); + Assert.assertTrue(bodyText.contains(_specimen_McMichael)); DataRegionTable mailTable = new DataRegionTable("EmailRecord", this, false, false); Assert.assertEquals("Notification was not as expected", notification, mailTable.getDataAsText(0, "Message")); } @@ -548,44 +582,45 @@ private void verifyNotificationEmails() Assert.fail("Failed to perform HTTP GET: "+e.getMessage()); } - clickLinkWithText("Request Link"); + clickAndWait(Locator.linkWithText("Request Link")); assertTextPresent("Specimen Request " + _requestId); } + @LogMethod private void verifyRequestCancel() { - clickLinkWithText("Update Request"); - selectOptionByText("status", "Not Yet Submitted"); + clickAndWait(Locator.linkWithText("Update Request")); + selectOptionByText(Locator.name("status"), "Not Yet Submitted"); clickButton("Save Changes and Send Notifications"); clickButton("Cancel Request", 0); Assert.assertTrue(getConfirmationAndWait().matches("^Canceling will permanently delete this pending request\\. Continue[\\s\\S]$")); assertTextPresent("No data to show."); - clickLinkWithText(getStudyLabel()); - clickLinkWithText("Specimen Data"); - clickLinkWithText("Vials by Derivative", false); + clickAndWait(Locator.linkWithText(getStudyLabel())); + clickAndWait(Locator.linkWithText("Specimen Data")); + click(Locator.linkWithText("Vials by Derivative")); waitAndClick(WAIT_FOR_JAVASCRIPT, Locator.linkWithText("Swab"), WAIT_FOR_PAGE); checkCheckbox(".toggle"); clickMenuButton("Request Options", "Create New Request"); clickButton("Cancel"); } + @LogMethod private void verifyReports() { log("check reports by participant group"); - clickLinkWithText(getFolderName()); - clickLinkWithText("Specimen Data"); - clickLinkWithText("Vials by Primary Type", false); + clickFolder(getFolderName()); + clickAndWait(Locator.linkWithText("Specimen Data")); waitAndClick(WAIT_FOR_JAVASCRIPT, Locator.linkWithText("Blood (Whole)"), WAIT_FOR_PAGE); pushLocation(); - clickLinkWithText("Reports"); + clickAndWait(Locator.linkWithText("Reports")); clickButton("View"); // Summary Report //Verify by vial count assertElementPresent(Locator.xpath("//a[number(text()) > 0]"), 36); - selectOptionByText("participantGroupFilter", "Category1"); + selectOptionByText(Locator.name("participantGroupFilter"), "Category1"); clickButton("Refresh"); assertElementNotPresent(Locator.xpath("//a[number(text()) > 6]")); assertElementPresent(Locator.xpath("//a[number(text()) <= 6]"), 8); - selectOptionByText("participantGroupFilter", "All Groups"); + selectOptionByText(Locator.name("participantGroupFilter"), "All Groups"); clickButton("Refresh"); assertElementPresent(Locator.xpath("//a[number(text()) > 0]"), 36); //Verify by ptid list @@ -594,22 +629,130 @@ private void verifyReports() clickButton("Refresh"); assertLinkPresentWithTextCount(PTIDS[0], 3); assertLinkPresentWithTextCount(PTIDS[1], 5); - selectOptionByText("participantGroupFilter", "Category1"); + selectOptionByText(Locator.name("participantGroupFilter"), "Category1"); clickButton("Refresh"); assertLinkPresentWithTextCount(PTIDS[0], 3); assertLinkPresentWithTextCount(PTIDS[1], 5); } + @LogMethod + private void verifyRequestingLocationRestriction() + { + clickFolder(getProjectName()); + clickFolder(getFolderName()); + + verifyRequestingLocationCounts(StudyLocationType.values()); // All locations should be enabled by default + + enableAndVerifyRequestingLocationTypes(); + enableAndVerifyRequestingLocationTypes(StudyLocationType.CLINIC); + enableAndVerifyRequestingLocationTypes(StudyLocationType.ENDPOINT); + enableAndVerifyRequestingLocationTypes(StudyLocationType.REPOSITORY); + enableAndVerifyRequestingLocationTypes(StudyLocationType.SAL); + enableAndVerifyRequestingLocationTypes(StudyLocationType.REPOSITORY, StudyLocationType.CLINIC); + enableAndVerifyRequestingLocationTypes(StudyLocationType.values()); + } + + /** + * Allow all provided location types to make requests, disallow all others + * @param types List of location types to allow to be requesting locations + */ + @LogMethod + private void enableAndVerifyRequestingLocationTypes(StudyLocationType... types) + { + clickTab("Manage"); + waitAndClick(Locator.linkWithText("Manage Location Types")); + waitForElement(Locator.id("labkey-nav-trail-current-page").withText("Manage Location Types")); + + for (StudyLocationType type : StudyLocationType.values()) + { + if (Arrays.asList(types).contains(type)) + _ext4Helper.checkCheckbox(type.toString()); + else + _ext4Helper.uncheckCheckbox(type.toString()); + } + + clickButton("Save", 0); + waitForElement(Locator.id("labkey-nav-trail-current-page").withText("Manage Study")); + + verifyRequestingLocationCounts(types); + } + + /** + * Verify that only permitted locations can submit specimen requests + * Location count algorithm is valid only for locations defined in sample_a.specimens + */ + private void verifyRequestingLocationCounts(StudyLocationType... types) + { + clickTab("Specimen Data"); + click(Locator.linkWithText("Specimen Requests")); + click(Locator.linkWithText("Create New Request")); + waitForElement(Locator.id("labkey-nav-trail-current-page").withText("New Specimen Request")); + + int expectedLocationCount = StudyLocationType.untypedSites(); + + long additionalLocations = Math.round(Math.pow(2, StudyLocationType.values().length - 1)); + + for (StudyLocationType type : StudyLocationType.values()) + { + if (Arrays.asList(types).contains(type)) + { + assertElementPresent(Locator.xpath("id('destinationSite')/option").containing(type.toString()), type.siteCount()); + expectedLocationCount += additionalLocations; + additionalLocations = additionalLocations / 2; // Each additional Location type adds less unique locations + } + } + + assertElementPresent(Locator.css("#destinationSite option"), expectedLocationCount + 1); // +1 for blank select option + + clickButton("Cancel", 0); + waitForElement(Locator.id("labkey-nav-trail-current-page").withText("Specimen Requests")); + } + + /** + * Provides info about locations defined in sample_a.specimens + */ + private enum StudyLocationType + { + REPOSITORY("Repository", 8), + CLINIC("Clinic", 8), + SAL("Site Affiliated Lab", 8), + ENDPOINT("Endpoint Lab", 8); + + private String _type; + private int _count; + + private StudyLocationType(String type, int count) + { + _type=type; + _count=count; + } + + public String toString() + { + return _type; + } + + public int siteCount() + { + return _count; + } + + public static int untypedSites() + { + return 9; + } + } + + @LogMethod private void searchTest() { goToProjectHome(); - clickLinkContainingText(getFolderName()); + clickAndWait(Locator.linkContainingText(getFolderName())); clickTab("Specimen Data"); - waitForPageToLoad(); waitForVialSearch(); - Ext4FieldRef additiveType = Ext4FieldRef.getForLabel(this, "Additive Type"); + Ext4FieldRefWD additiveType = Ext4FieldRefWD.getForLabel(this, "Additive Type"); additiveType.setValue("Heparin"); - Ext4FieldRef.getForLabel(this, "Participant").setValue("999320812"); + Ext4FieldRefWD.getForLabel(this, "Participant").setValue("999320812"); clickButtonContainingText("Search"); assertTextNotPresent("Serum Separator"); assertTextPresent("(ParticipantId = 999320812) AND (AdditiveType = Heparin)"); @@ -630,6 +773,7 @@ private void waitForVialSearch() waitForElement(Locator.css(".specimenSearchLoaded")); } + @LogMethod private void exportSpecimenTest() { popLocation(); @@ -639,8 +783,8 @@ private void exportSpecimenTest() goToAuditLog(); - selectOptionByText("view", "Query events"); - waitForPageToLoad(); + selectOptionByText(Locator.name("view"), "Query events"); + waitForElement(Locator.id("dataregion_audit")); DataRegionTable auditTable = new DataRegionTable("audit", this); String[][] columnAndValues = new String[][] {{"Created By", getDisplayName()}, From 7232b252b5262cf1fc08a19f12e2144ed832a2e7 Mon Sep 17 00:00:00 2001 From: Trey Chadick Date: Wed, 26 Dec 2012 19:26:57 +0000 Subject: [PATCH 072/494] Refactor to use WebDriver alert helper SVN r23773 |2012-12-26 19:26:57 +0000 --- src/org/labkey/test/tests/SpecimenTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/org/labkey/test/tests/SpecimenTest.java b/src/org/labkey/test/tests/SpecimenTest.java index bd30aa20..be01af7a 100644 --- a/src/org/labkey/test/tests/SpecimenTest.java +++ b/src/org/labkey/test/tests/SpecimenTest.java @@ -593,8 +593,8 @@ private void verifyRequestCancel() selectOptionByText(Locator.name("status"), "Not Yet Submitted"); clickButton("Save Changes and Send Notifications"); clickButton("Cancel Request", 0); - Assert.assertTrue(getConfirmationAndWait().matches("^Canceling will permanently delete this pending request\\. Continue[\\s\\S]$")); - assertTextPresent("No data to show."); + assertAlert("Canceling will permanently delete this pending request. Continue?"); + waitForText("No data to show."); clickAndWait(Locator.linkWithText(getStudyLabel())); clickAndWait(Locator.linkWithText("Specimen Data")); click(Locator.linkWithText("Vials by Derivative")); From 92e89cba7832e175248cd4cfbce93900d98d6279 Mon Sep 17 00:00:00 2001 From: Trey Chadick Date: Wed, 26 Dec 2012 23:23:29 +0000 Subject: [PATCH 073/494] Add test for specimen notification specimen list setting Refactor tab helpers to use Locator SVN r23776 |2012-12-26 23:23:29 +0000 --- src/org/labkey/test/tests/SpecimenTest.java | 98 ++++++++++++++++++--- 1 file changed, 84 insertions(+), 14 deletions(-) diff --git a/src/org/labkey/test/tests/SpecimenTest.java b/src/org/labkey/test/tests/SpecimenTest.java index be01af7a..c1214b8a 100644 --- a/src/org/labkey/test/tests/SpecimenTest.java +++ b/src/org/labkey/test/tests/SpecimenTest.java @@ -26,6 +26,7 @@ import org.labkey.test.util.PasswordUtil; import org.labkey.test.util.ext4cmp.Ext4FieldRefWD; +import java.io.File; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.Arrays; @@ -47,6 +48,7 @@ public class SpecimenTest extends StudyBaseTestWD private static final String USER2 = "user2@specimen.test"; private static final String REQUESTABILITY_QUERY = "RequestabilityRule"; private static final String UNREQUESTABLE_SAMPLE = "BAA07XNP-02"; + private final File REQUEST_ATTACHMENT = new File(getPipelinePath() + "specimens", "labs.txt"); private static final String[] PTIDS = {"999320396","999320812"}; private int _requestId; @@ -106,6 +108,7 @@ protected void doVerifySteps() verifyReports(); exportSpecimenTest(); verifyRequestingLocationRestriction(); + verifySpecimenTableAttachments(); searchTest(); } @@ -126,7 +129,7 @@ private void setupRequestabilityRules() clickButton("Save", 0); waitForText("Saved", WAIT_FOR_JAVASCRIPT); - clickAndWait(Locator.linkWithText(getStudyLabel())); + clickAndWait(Locator.linkWithText(getFolderName())); waitAndClick(Locator.linkWithText("Manage Study")); waitAndClick(Locator.linkWithText("Manage Requestability Rules")); // Verify that LOCKED_IN_REQUEST is the last rule @@ -373,6 +376,12 @@ private void createRequest() assertTextNotPresent("Not Yet Submitted"); assertTextPresent("New Request"); + // Add request attachment + click(Locator.linkWithText("Update Request")); + setFormElement(Locator.name("formFiles[0]"), REQUEST_ATTACHMENT); + clickButton("Save Changes and Send Notifications"); + waitForElement(Locator.linkWithText(" " + REQUEST_ATTACHMENT.getName())); + // modify request selectOptionByText(Locator.name("newActor"), "SLG"); setFormElement(Locator.name("newDescription"), "Other SLG Approval"); @@ -382,7 +391,7 @@ private void createRequest() checkCheckbox("notificationIdPairs"); checkCheckbox("notificationIdPairs", 1); clickButton("Save Changes and Send Notifications"); - assertTextPresent("Complete"); + waitForElement(Locator.css(".labkey-message").withText("Complete")); } @LogMethod @@ -514,18 +523,18 @@ private void verifyAdditionalRequestFields() "sample last one input\n" + "Specimen List (Request Link)\n" + "\n" + - " Participant Id Global Unique Id Visit Description Visit Volume Volume Units Primary Type Derivative Type Additive Type Derivative Type2 Sub Additive Derivative Draw Timestamp Clinic Processing Location First Processed By Initials Sal Receipt Date Class Id Protocol Number Primary Volume Primary Volume Units Total Cell Count Tube Type Comments Locked In Request Requestable Site Name Site Ldms Code At Repository Available Availability Reason Quality Control Flag Quality Control Comments Collection Cohort Vial Count Locked In Request Count At Repository Count Available Count Expected Available Count\n" + - "1 999320824 BAA07XNP-01 Vst 1.0 ML Blood (Whole) Plasma, Unknown Processing EDTA N/A 2005-12-23 10:05:00.0 KCMC, Moshi, Tanzania Contract Lab Services, Johannesburg, South Africa LK 2005-12-23 00:00:00.0 LABK 39 15ml Cryovial true Contract Lab Services, Johannesburg, South Africa 350 true false This vial is unavailable because it is locked in a specimen request. false 2 1 2 0 1\n" + - "2 999320087 CAA07XN8-01 Vst 1.0 ML Vaginal Swab Swab None N/A 2005-12-22 12:50:00.0 KCMC, Moshi, Tanzania Contract Lab Services, Johannesburg, South Africa LK 2005-12-22 00:00:00.0 LABK 39 15ml Cryovial true Contract Lab Services, Johannesburg, South Africa 350 true false This vial is unavailable because it is locked in a specimen request. false 1 1 1 0 0\n" + - "3 999320706 DAA07YGW-01 Vst 1.0 ML Vaginal Swab Swab None N/A 2006-01-05 10:00:00.0 KCMC, Moshi, Tanzania Contract Lab Services, Johannesburg, South Africa LK 2006-01-05 00:00:00.0 LABK 39 15ml Cryovial true Contract Lab Services, Johannesburg, South Africa 350 true false This vial is unavailable because it is locked in a specimen request. false 1 1 1 0 0\n" + - "4 999320898 FAA07XLJ-01 Vst 1.0 ML Vaginal Swab Swab None N/A 2005-12-20 12:05:00.0 KCMC, Moshi, Tanzania Contract Lab Services, Johannesburg, South Africa LK 2005-12-20 00:00:00.0 LABK 39 15ml Cryovial true Contract Lab Services, Johannesburg, South Africa 350 true false This vial is unavailable because it is locked in a specimen request. false 1 1 1 0 0\n" + - "5 999320264 FAA07YSC-01 Vst 1.0 ML Vaginal Swab Swab None N/A 2006-01-13 12:10:00.0 KCMC, Moshi, Tanzania Contract Lab Services, Johannesburg, South Africa LK 2006-01-13 00:00:00.0 LABK 39 15ml Cryovial true Contract Lab Services, Johannesburg, South Africa 350 true false This vial is unavailable because it is locked in a specimen request. false 1 1 1 0 0\n" + - "6 999320520 FAA07YXY-01 Vst 1.0 ML Vaginal Swab Swab None N/A 2005-12-15 10:30:00.0 KCMC, Moshi, Tanzania Contract Lab Services, Johannesburg, South Africa LK 2006-01-15 00:00:00.0 LABK 39 15ml Cryovial true Contract Lab Services, Johannesburg, South Africa 350 true false This vial is unavailable because it is locked in a specimen request. false 1 1 1 0 0\n" + - "7 999320498 JAA07YJB-01 Vst 1.0 ML Vaginal Swab Swab None N/A 2006-01-05 09:30:00.0 KCMC, Moshi, Tanzania Contract Lab Services, Johannesburg, South Africa LK 2006-01-11 00:00:00.0 LABK 39 15ml Cryovial true Contract Lab Services, Johannesburg, South Africa 350 true false This vial is unavailable because it is locked in a specimen request. false 1 1 1 0 0\n" + - "8 999320476 JAA07YSQ-01 Vst 1.0 ML Vaginal Swab Swab None N/A 2006-01-11 10:20:00.0 KCMC, Moshi, Tanzania Contract Lab Services, Johannesburg, South Africa LK 2006-01-11 00:00:00.0 LABK 39 15ml Cryovial true Contract Lab Services, Johannesburg, South Africa 350 true false This vial is unavailable because it is locked in a specimen request. false 1 1 1 0 0\n" + - "9 999320980 KAA07YV1-01 Vst 1.0 ML Vaginal Swab Swab None N/A 2006-01-17 08:30:00.0 KCMC, Moshi, Tanzania Contract Lab Services, Johannesburg, South Africa LK 2006-01-17 00:00:00.0 LABK 39 15ml Cryovial true Contract Lab Services, Johannesburg, South Africa 350 true false This vial is unavailable because it is locked in a specimen request. false 1 1 1 0 0\n" + - "10 999320520 KAA07YY0-01 Vst 1.0 ML Blood (Whole) Plasma, Unknown Processing EDTA N/A 2005-12-15 10:30:00.0 KCMC, Moshi, Tanzania Contract Lab Services, Johannesburg, South Africa LK 2006-01-15 00:00:00.0 LABK 39 15ml Cryovial true Contract Lab Services, Johannesburg, South Africa 350 true false This vial is unavailable because it is locked in a specimen request. false 2 2 2 0 0\n" + - "11 999320520 KAA07YY0-02 Vst 1.0 ML Blood (Whole) Plasma, Unknown Processing EDTA N/A 2005-12-15 10:30:00.0 KCMC, Moshi, Tanzania Contract Lab Services, Johannesburg, South Africa LK 2006-01-15 00:00:00.0 LABK 39 15ml Cryovial true Contract Lab Services, Johannesburg, South Africa 350 true false This vial is unavailable because it is locked in a specimen request. false 2 2 2 0 0"; + " Participant Id Global Unique Id Visit Description Sequence Num Visit Volume Volume Units Primary Type Derivative Type Additive Type Derivative Type2 Sub Additive Derivative Draw Timestamp Clinic Processing Location First Processed By Initials Sal Receipt Date Class Id Protocol Number Primary Volume Primary Volume Units Total Cell Count Tube Type Comments Locked In Request Requestable Site Name Site Ldms Code At Repository Available Availability Reason Quality Control Flag Quality Control Comments Collection Cohort Vial Count Locked In Request Count At Repository Count Available Count Expected Available Count\n" + + "1 999320824 BAA07XNP-01 Vst 501.0 1.0 ML Blood (Whole) Plasma, Unknown Processing EDTA N/A 2005-12-23 10:05:00.0 KCMC, Moshi, Tanzania Contract Lab Services, Johannesburg, South Africa LK 2005-12-23 00:00:00.0 LABK 39 15ml Cryovial true Contract Lab Services, Johannesburg, South Africa 350 true false This vial is unavailable because it is locked in a specimen request. false 2 1 2 0 1\n" + + "2 999320087 CAA07XN8-01 Vst 301.0 1.0 ML Vaginal Swab Swab None N/A 2005-12-22 12:50:00.0 KCMC, Moshi, Tanzania Contract Lab Services, Johannesburg, South Africa LK 2005-12-22 00:00:00.0 LABK 39 15ml Cryovial true Contract Lab Services, Johannesburg, South Africa 350 true false This vial is unavailable because it is locked in a specimen request. false 1 1 1 0 0\n" + + "3 999320706 DAA07YGW-01 Vst 301.0 1.0 ML Vaginal Swab Swab None N/A 2006-01-05 10:00:00.0 KCMC, Moshi, Tanzania Contract Lab Services, Johannesburg, South Africa LK 2006-01-05 00:00:00.0 LABK 39 15ml Cryovial true Contract Lab Services, Johannesburg, South Africa 350 true false This vial is unavailable because it is locked in a specimen request. false 1 1 1 0 0\n" + + "4 999320898 FAA07XLJ-01 Vst 301.0 1.0 ML Vaginal Swab Swab None N/A 2005-12-20 12:05:00.0 KCMC, Moshi, Tanzania Contract Lab Services, Johannesburg, South Africa LK 2005-12-20 00:00:00.0 LABK 39 15ml Cryovial true Contract Lab Services, Johannesburg, South Africa 350 true false This vial is unavailable because it is locked in a specimen request. false 1 1 1 0 0\n" + + "5 999320264 FAA07YSC-01 Vst 201.0 1.0 ML Vaginal Swab Swab None N/A 2006-01-13 12:10:00.0 KCMC, Moshi, Tanzania Contract Lab Services, Johannesburg, South Africa LK 2006-01-13 00:00:00.0 LABK 39 15ml Cryovial true Contract Lab Services, Johannesburg, South Africa 350 true false This vial is unavailable because it is locked in a specimen request. false 1 1 1 0 0\n" + + "6 999320520 FAA07YXY-01 Vst 501.0 1.0 ML Vaginal Swab Swab None N/A 2005-12-15 10:30:00.0 KCMC, Moshi, Tanzania Contract Lab Services, Johannesburg, South Africa LK 2006-01-15 00:00:00.0 LABK 39 15ml Cryovial true Contract Lab Services, Johannesburg, South Africa 350 true false This vial is unavailable because it is locked in a specimen request. false 1 1 1 0 0\n" + + "7 999320498 JAA07YJB-01 Vst 501.0 1.0 ML Vaginal Swab Swab None N/A 2006-01-05 09:30:00.0 KCMC, Moshi, Tanzania Contract Lab Services, Johannesburg, South Africa LK 2006-01-11 00:00:00.0 LABK 39 15ml Cryovial true Contract Lab Services, Johannesburg, South Africa 350 true false This vial is unavailable because it is locked in a specimen request. false 1 1 1 0 0\n" + + "8 999320476 JAA07YSQ-01 Vst 301.0 1.0 ML Vaginal Swab Swab None N/A 2006-01-11 10:20:00.0 KCMC, Moshi, Tanzania Contract Lab Services, Johannesburg, South Africa LK 2006-01-11 00:00:00.0 LABK 39 15ml Cryovial true Contract Lab Services, Johannesburg, South Africa 350 true false This vial is unavailable because it is locked in a specimen request. false 1 1 1 0 0\n" + + "9 999320980 KAA07YV1-01 Vst 301.0 1.0 ML Vaginal Swab Swab None N/A 2006-01-17 08:30:00.0 KCMC, Moshi, Tanzania Contract Lab Services, Johannesburg, South Africa LK 2006-01-17 00:00:00.0 LABK 39 15ml Cryovial true Contract Lab Services, Johannesburg, South Africa 350 true false This vial is unavailable because it is locked in a specimen request. false 1 1 1 0 0\n" + + "10 999320520 KAA07YY0-01 Vst 501.0 1.0 ML Blood (Whole) Plasma, Unknown Processing EDTA N/A 2005-12-15 10:30:00.0 KCMC, Moshi, Tanzania Contract Lab Services, Johannesburg, South Africa LK 2006-01-15 00:00:00.0 LABK 39 15ml Cryovial true Contract Lab Services, Johannesburg, South Africa 350 true false This vial is unavailable because it is locked in a specimen request. false 2 2 2 0 0\n" + + "11 999320520 KAA07YY0-02 Vst 501.0 1.0 ML Blood (Whole) Plasma, Unknown Processing EDTA N/A 2005-12-15 10:30:00.0 KCMC, Moshi, Tanzania Contract Lab Services, Johannesburg, South Africa LK 2006-01-15 00:00:00.0 LABK 39 15ml Cryovial true Contract Lab Services, Johannesburg, South Africa 350 true false This vial is unavailable because it is locked in a specimen request. false 2 2 2 0 0"; @LogMethod private void verifyNotificationEmails() { @@ -652,6 +661,67 @@ private void verifyRequestingLocationRestriction() enableAndVerifyRequestingLocationTypes(StudyLocationType.values()); } + @LogMethod + private void verifySpecimenTableAttachments() + { + clickFolder(getProjectName()); + clickFolder(getFolderName()); + + log("Setup Excel specimen attachment"); + clickAndWait(Locator.linkWithText("Manage")); + clickAndWait(Locator.linkWithText("Manage Notifications")); + checkCheckbox(Locator.checkboxById("newRequestNotifyCheckbox")); + setFormElement(Locator.id("newRequestNotify"), PasswordUtil.getUsername()); + checkRadioButton(Locator.radioButtonByNameAndValue("specimensAttachment", "ExcelAttachment")); + clickButton("Save"); + + log("Create request with excel specimen attachment"); + clickTab("Specimen Data"); + clickAndWait(Locator.linkWithText("Urine")); + checkDataRegionCheckbox("SpecimenDetail", 0); + _extHelper.clickMenuButton(true, "Request Options", "Create New Request"); + selectOptionByText(Locator.name("destinationSite"), DESTINATION_SITE); + setFormElement(Locator.id("input0"), "Assay Plan"); + setFormElement(Locator.id("input1"), "Shipping"); + setFormElement(Locator.id("input3"), "Comments"); + clickButton("Create and View Details"); + clickButton("Submit Request", 0); + getConfirmationAndWait(); + waitForElement(Locator.css("h3").withText("Your request has been successfully submitted.")); + + log("Setup text specimen attachment"); + clickAndWait(Locator.linkWithText("Manage")); + clickAndWait(Locator.linkWithText("Manage Notifications")); + checkRadioButton(Locator.radioButtonByNameAndValue("specimensAttachment", "TextAttachment")); + clickButton("Save"); + + log("Create request with text specimen attachment"); + clickTab("Specimen Data"); + clickAndWait(Locator.linkWithText("Urine")); + checkDataRegionCheckbox("SpecimenDetail", 1); + _extHelper.clickMenuButton(true, "Request Options", "Create New Request"); + selectOptionByText(Locator.name("destinationSite"), DESTINATION_SITE); + setFormElement(Locator.id("input0"), "Assay Plan"); + setFormElement(Locator.id("input1"), "Shipping"); + setFormElement(Locator.id("input3"), "Comments"); + clickButton("Create and View Details"); + clickButton("Submit Request", 0); + getConfirmationAndWait(); + waitForElement(Locator.css("h3").withText("Your request has been successfully submitted.")); + + log("Verify specimen list attachments"); + goToModule("Dumbster"); + + click(Locator.linkContainingText("Specimen Request Notification")); + waitForElement(Locator.linkWithText("SpecimenDetail.tsv")); + click(Locator.linkContainingText("Specimen Request Notification").index(1)); + waitForElement(Locator.linkWithText("SpecimenDetail.xls")); + + // Each notification should be have only the specimen request details, no specimen list + assertElementPresent(Locator.css("#email_body_1 > table"), 1); + assertElementPresent(Locator.css("#email_body_2 > table"), 1); + } + /** * Allow all provided location types to make requests, disallow all others * @param types List of location types to allow to be requesting locations From ba3ea92cb0ca7c0d278f13a95f680d7137153cbc Mon Sep 17 00:00:00 2001 From: Trey Chadick Date: Wed, 2 Jan 2013 21:16:32 +0000 Subject: [PATCH 074/494] Wait for form to render SVN r23820 |2013-01-02 21:16:32 +0000 --- src/org/labkey/test/tests/SpecimenTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/org/labkey/test/tests/SpecimenTest.java b/src/org/labkey/test/tests/SpecimenTest.java index c1214b8a..2cd7ac82 100644 --- a/src/org/labkey/test/tests/SpecimenTest.java +++ b/src/org/labkey/test/tests/SpecimenTest.java @@ -378,6 +378,7 @@ private void createRequest() // Add request attachment click(Locator.linkWithText("Update Request")); + waitForElement(Locator.name("formFiles[0]")); setFormElement(Locator.name("formFiles[0]"), REQUEST_ATTACHMENT); clickButton("Save Changes and Send Notifications"); waitForElement(Locator.linkWithText(" " + REQUEST_ATTACHMENT.getName())); From 08a21a3f3f44fb6922b99fe29ca5ab479ec090fe Mon Sep 17 00:00:00 2001 From: Trey Chadick Date: Thu, 3 Jan 2013 21:23:12 +0000 Subject: [PATCH 075/494] Try to prevent timeout when fetching body text SVN r23836 |2013-01-03 21:23:12 +0000 --- src/org/labkey/test/tests/SpecimenTest.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/org/labkey/test/tests/SpecimenTest.java b/src/org/labkey/test/tests/SpecimenTest.java index 2cd7ac82..3eeef55c 100644 --- a/src/org/labkey/test/tests/SpecimenTest.java +++ b/src/org/labkey/test/tests/SpecimenTest.java @@ -25,6 +25,7 @@ import org.labkey.test.util.LogMethod; import org.labkey.test.util.PasswordUtil; import org.labkey.test.util.ext4cmp.Ext4FieldRefWD; +import org.openqa.selenium.support.ui.ExpectedConditions; import java.io.File; import java.io.IOException; @@ -557,6 +558,7 @@ private void verifyNotificationEmails() Assert.assertTrue(bodyText.contains(_specimen_McMichael)); Assert.assertTrue(!bodyText.contains(_specimen_KCMC)); click(Locator.linkContainingText("Specimen Request Notification").index(1)); + _shortWait.until(ExpectedConditions.visibilityOf(Locator.id("email_body_2").findElement(_driver))); bodyText = getBodyText(); Assert.assertTrue(bodyText.contains(_specimen_KCMC)); DataRegionTable mailTable = new DataRegionTable("EmailRecord", this, false, false); @@ -569,6 +571,7 @@ private void verifyNotificationEmails() Assert.assertTrue(bodyText.contains(_specimen_KCMC)); Assert.assertTrue(!bodyText.contains(_specimen_McMichael)); click(Locator.linkContainingText("Specimen Request Notification").index(1)); + _shortWait.until(ExpectedConditions.visibilityOf(Locator.id("email_body_2").findElement(_driver))); bodyText = getBodyText(); Assert.assertTrue(bodyText.contains(_specimen_McMichael)); DataRegionTable mailTable = new DataRegionTable("EmailRecord", this, false, false); From 73bce16fe3e6ff5e7190c6442a51a8a6f8d7e6af Mon Sep 17 00:00:00 2001 From: Trey Chadick Date: Fri, 4 Jan 2013 17:45:41 +0000 Subject: [PATCH 076/494] Wait for everybody SVN r23848 |2013-01-04 17:45:41 +0000 --- src/org/labkey/test/tests/SpecimenTest.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/org/labkey/test/tests/SpecimenTest.java b/src/org/labkey/test/tests/SpecimenTest.java index 3eeef55c..d208d00b 100644 --- a/src/org/labkey/test/tests/SpecimenTest.java +++ b/src/org/labkey/test/tests/SpecimenTest.java @@ -554,6 +554,7 @@ private void verifyNotificationEmails() if (getTableCellText("dataregion_EmailRecord", 2, 0).equals(USER1)) { click(Locator.linkContainingText("Specimen Request Notification")); + _shortWait.until(ExpectedConditions.visibilityOf(Locator.id("email_body_1").findElement(_driver))); String bodyText = getBodyText(); Assert.assertTrue(bodyText.contains(_specimen_McMichael)); Assert.assertTrue(!bodyText.contains(_specimen_KCMC)); @@ -567,6 +568,7 @@ private void verifyNotificationEmails() else { click(Locator.linkContainingText("Specimen Request Notification")); + _shortWait.until(ExpectedConditions.visibilityOf(Locator.id("email_body_1").findElement(_driver))); String bodyText = getBodyText(); Assert.assertTrue(bodyText.contains(_specimen_KCMC)); Assert.assertTrue(!bodyText.contains(_specimen_McMichael)); From 352dd8c31cb0985529b1b1cf928484f99d946354 Mon Sep 17 00:00:00 2001 From: Dave Bradlee Date: Tue, 8 Jan 2013 00:53:31 +0000 Subject: [PATCH 077/494] Bug 16839: changes "Site" to "Location" in most places. Exceptions are Study.Site table will remain as is. Also, a couple of columns in Study.Participant still use the term Site. SVN r23898 |2013-01-08 00:53:31 +0000 --- src/org/labkey/test/tests/SpecimenTest.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/org/labkey/test/tests/SpecimenTest.java b/src/org/labkey/test/tests/SpecimenTest.java index d208d00b..1e363266 100644 --- a/src/org/labkey/test/tests/SpecimenTest.java +++ b/src/org/labkey/test/tests/SpecimenTest.java @@ -252,7 +252,7 @@ private void uploadSpecimensFromFile() clickAndWait(Locator.linkWithText("Specimen Data")); waitAndClick(Locator.linkWithText("Specimen Requests")); // expand node in Specimens webpart clickAndWait(Locator.linkWithText("Create New Request")); - selectOptionByText(Locator.name("destinationSite"), DESTINATION_SITE); + selectOptionByText(Locator.name("destinationLocation"), DESTINATION_SITE); setFormElement(Locator.id("input0"), "Assay Plan"); setFormElement(Locator.id("input2"), "Comments"); setFormElement(Locator.id("input1"), "Shipping"); @@ -318,7 +318,7 @@ private void createRequest() goBack(); clickMenuButton("Request Options", "Create New Request"); - selectOptionByText(Locator.name("destinationSite"), DESTINATION_SITE); + selectOptionByText(Locator.name("destinationLocation"), DESTINATION_SITE); setFormElement(Locator.id("input0"), "Assay Plan"); setFormElement(Locator.id("input2"), "Comments"); setFormElement(Locator.id("input1"), "Shipping"); @@ -686,7 +686,7 @@ private void verifySpecimenTableAttachments() clickAndWait(Locator.linkWithText("Urine")); checkDataRegionCheckbox("SpecimenDetail", 0); _extHelper.clickMenuButton(true, "Request Options", "Create New Request"); - selectOptionByText(Locator.name("destinationSite"), DESTINATION_SITE); + selectOptionByText(Locator.name("destinationLocation"), DESTINATION_SITE); setFormElement(Locator.id("input0"), "Assay Plan"); setFormElement(Locator.id("input1"), "Shipping"); setFormElement(Locator.id("input3"), "Comments"); @@ -706,7 +706,7 @@ private void verifySpecimenTableAttachments() clickAndWait(Locator.linkWithText("Urine")); checkDataRegionCheckbox("SpecimenDetail", 1); _extHelper.clickMenuButton(true, "Request Options", "Create New Request"); - selectOptionByText(Locator.name("destinationSite"), DESTINATION_SITE); + selectOptionByText(Locator.name("destinationLocation"), DESTINATION_SITE); setFormElement(Locator.id("input0"), "Assay Plan"); setFormElement(Locator.id("input1"), "Shipping"); setFormElement(Locator.id("input3"), "Comments"); @@ -772,13 +772,13 @@ private void verifyRequestingLocationCounts(StudyLocationType... types) { if (Arrays.asList(types).contains(type)) { - assertElementPresent(Locator.xpath("id('destinationSite')/option").containing(type.toString()), type.siteCount()); + assertElementPresent(Locator.xpath("id('destinationLocation')/option").containing(type.toString()), type.siteCount()); expectedLocationCount += additionalLocations; additionalLocations = additionalLocations / 2; // Each additional Location type adds less unique locations } } - assertElementPresent(Locator.css("#destinationSite option"), expectedLocationCount + 1); // +1 for blank select option + assertElementPresent(Locator.css("#destinationLocation option"), expectedLocationCount + 1); // +1 for blank select option clickButton("Cancel", 0); waitForElement(Locator.id("labkey-nav-trail-current-page").withText("Specimen Requests")); From 7edd41e6a5f049d5b5e75a414500edcceb4bfaa5 Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Tue, 8 Jan 2013 05:59:14 +0000 Subject: [PATCH 078/494] Update licenses SVN r23902 |2013-01-08 05:59:14 +0000 --- src/org/labkey/test/tests/SpecimenTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/org/labkey/test/tests/SpecimenTest.java b/src/org/labkey/test/tests/SpecimenTest.java index 1e363266..cc59e757 100644 --- a/src/org/labkey/test/tests/SpecimenTest.java +++ b/src/org/labkey/test/tests/SpecimenTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007-2012 LabKey Corporation + * Copyright (c) 2007-2013 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. From 4862478d3797020e4152599ccbd06bab2674e6d2 Mon Sep 17 00:00:00 2001 From: Trey Chadick Date: Thu, 10 Jan 2013 17:26:36 +0000 Subject: [PATCH 079/494] Try to fix WebDriver timeout SVN r23961 |2013-01-10 17:26:36 +0000 --- src/org/labkey/test/tests/SpecimenTest.java | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/org/labkey/test/tests/SpecimenTest.java b/src/org/labkey/test/tests/SpecimenTest.java index cc59e757..678cffec 100644 --- a/src/org/labkey/test/tests/SpecimenTest.java +++ b/src/org/labkey/test/tests/SpecimenTest.java @@ -16,15 +16,20 @@ package org.labkey.test.tests; +import org.jetbrains.annotations.Nullable; import org.junit.Assert; import org.apache.http.HttpException; import org.apache.http.HttpStatus; import org.labkey.test.Locator; import org.labkey.test.WebTestHelper; import org.labkey.test.util.DataRegionTable; +import org.labkey.test.util.LabKeyExpectedConditions; import org.labkey.test.util.LogMethod; import org.labkey.test.util.PasswordUtil; import org.labkey.test.util.ext4cmp.Ext4FieldRefWD; +import org.openqa.selenium.By; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.support.ui.ExpectedCondition; import org.openqa.selenium.support.ui.ExpectedConditions; import java.io.File; @@ -554,12 +559,12 @@ private void verifyNotificationEmails() if (getTableCellText("dataregion_EmailRecord", 2, 0).equals(USER1)) { click(Locator.linkContainingText("Specimen Request Notification")); - _shortWait.until(ExpectedConditions.visibilityOf(Locator.id("email_body_1").findElement(_driver))); + _shortWait.until(LabKeyExpectedConditions.emailIsExpanded(1)); String bodyText = getBodyText(); Assert.assertTrue(bodyText.contains(_specimen_McMichael)); Assert.assertTrue(!bodyText.contains(_specimen_KCMC)); click(Locator.linkContainingText("Specimen Request Notification").index(1)); - _shortWait.until(ExpectedConditions.visibilityOf(Locator.id("email_body_2").findElement(_driver))); + _shortWait.until(LabKeyExpectedConditions.emailIsExpanded(2)); bodyText = getBodyText(); Assert.assertTrue(bodyText.contains(_specimen_KCMC)); DataRegionTable mailTable = new DataRegionTable("EmailRecord", this, false, false); @@ -568,12 +573,12 @@ private void verifyNotificationEmails() else { click(Locator.linkContainingText("Specimen Request Notification")); - _shortWait.until(ExpectedConditions.visibilityOf(Locator.id("email_body_1").findElement(_driver))); + _shortWait.until(LabKeyExpectedConditions.emailIsExpanded(1)); String bodyText = getBodyText(); Assert.assertTrue(bodyText.contains(_specimen_KCMC)); Assert.assertTrue(!bodyText.contains(_specimen_McMichael)); click(Locator.linkContainingText("Specimen Request Notification").index(1)); - _shortWait.until(ExpectedConditions.visibilityOf(Locator.id("email_body_2").findElement(_driver))); + _shortWait.until(LabKeyExpectedConditions.emailIsExpanded(2)); bodyText = getBodyText(); Assert.assertTrue(bodyText.contains(_specimen_McMichael)); DataRegionTable mailTable = new DataRegionTable("EmailRecord", this, false, false); From 42f49e875910f932762368e3a62f2e0540cb7cfb Mon Sep 17 00:00:00 2001 From: Trey Chadick Date: Thu, 10 Jan 2013 22:39:34 +0000 Subject: [PATCH 080/494] Checkpoint for new waitForPageToLoad method Simplify some test code SVN r23973 |2013-01-10 22:39:34 +0000 --- src/org/labkey/test/tests/SpecimenTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/org/labkey/test/tests/SpecimenTest.java b/src/org/labkey/test/tests/SpecimenTest.java index 678cffec..48ecb639 100644 --- a/src/org/labkey/test/tests/SpecimenTest.java +++ b/src/org/labkey/test/tests/SpecimenTest.java @@ -378,7 +378,7 @@ private void createRequest() assertTextPresent("Not Yet Submitted"); assertTextNotPresent("New Request"); clickButton("Submit Request", 0); - Assert.assertTrue(getConfirmationAndWait().matches("^Once a request is submitted, its specimen list may no longer be modified\\. Continue[\\s\\S]$")); + assertAlert("Once a request is submitted, its specimen list may no longer be modified. Continue?"); assertTextNotPresent("Not Yet Submitted"); assertTextPresent("New Request"); @@ -697,7 +697,7 @@ private void verifySpecimenTableAttachments() setFormElement(Locator.id("input3"), "Comments"); clickButton("Create and View Details"); clickButton("Submit Request", 0); - getConfirmationAndWait(); + getAlert(); waitForElement(Locator.css("h3").withText("Your request has been successfully submitted.")); log("Setup text specimen attachment"); @@ -717,7 +717,7 @@ private void verifySpecimenTableAttachments() setFormElement(Locator.id("input3"), "Comments"); clickButton("Create and View Details"); clickButton("Submit Request", 0); - getConfirmationAndWait(); + getAlert(); waitForElement(Locator.css("h3").withText("Your request has been successfully submitted.")); log("Verify specimen list attachments"); From 882d17e5823fb6cdad3056e4e79000088ab79473 Mon Sep 17 00:00:00 2001 From: Nick Kerr Date: Fri, 11 Jan 2013 02:30:22 +0000 Subject: [PATCH 081/494] ITN: Manage subcategories. SVN r23979 |2013-01-11 02:30:22 +0000 --- src/org/labkey/test/tests/SpecimenTest.java | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/org/labkey/test/tests/SpecimenTest.java b/src/org/labkey/test/tests/SpecimenTest.java index 48ecb639..bb2a031d 100644 --- a/src/org/labkey/test/tests/SpecimenTest.java +++ b/src/org/labkey/test/tests/SpecimenTest.java @@ -16,10 +16,9 @@ package org.labkey.test.tests; -import org.jetbrains.annotations.Nullable; -import org.junit.Assert; import org.apache.http.HttpException; import org.apache.http.HttpStatus; +import org.junit.Assert; import org.labkey.test.Locator; import org.labkey.test.WebTestHelper; import org.labkey.test.util.DataRegionTable; @@ -27,10 +26,6 @@ import org.labkey.test.util.LogMethod; import org.labkey.test.util.PasswordUtil; import org.labkey.test.util.ext4cmp.Ext4FieldRefWD; -import org.openqa.selenium.By; -import org.openqa.selenium.WebDriver; -import org.openqa.selenium.support.ui.ExpectedCondition; -import org.openqa.selenium.support.ui.ExpectedConditions; import java.io.File; import java.io.IOException; From ccc1579e4710b7211f342e7525ab5f8a80c3e4c0 Mon Sep 17 00:00:00 2001 From: Dave Bradlee Date: Sun, 20 Jan 2013 15:50:11 +0000 Subject: [PATCH 082/494] Change to allow Vial groupings in the specimen web part to be customized. SVN r24166 |2013-01-20 15:50:11 +0000 --- src/org/labkey/test/tests/SpecimenTest.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/org/labkey/test/tests/SpecimenTest.java b/src/org/labkey/test/tests/SpecimenTest.java index bb2a031d..2efdf903 100644 --- a/src/org/labkey/test/tests/SpecimenTest.java +++ b/src/org/labkey/test/tests/SpecimenTest.java @@ -171,7 +171,7 @@ private void checkTubeType() assertTextPresent("20ml Cryovial"); assertTextPresent("25ml Cryovial"); clickAndWait(Locator.linkWithText("Specimen Overview")); - click(Locator.linkWithText("Vials by Derivative")); + click(Locator.linkWithText("Vials by DerivativeType")); waitAndClick(WAIT_FOR_JAVASCRIPT, Locator.linkWithText("Tear Flo Strips"), WAIT_FOR_PAGE); // For these three vials, there should be no conflict in TubeType, so we should see the text once for each of three vials: assertLinkPresentWithTextCount("[history]", 3); @@ -304,7 +304,7 @@ private void createRequest() { clickAndWait(Locator.linkWithText(getStudyLabel())); clickAndWait(Locator.linkWithText("Specimen Data")); - click(Locator.linkWithText("Vials by Derivative")); + click(Locator.linkWithText("Vials by DerivativeType")); waitAndClick(WAIT_FOR_JAVASCRIPT, Locator.linkWithText("Plasma, Unknown Processing"), WAIT_FOR_PAGE); // Verify unavailable sample assertElementPresent(Locator.xpath("//input[@id='check_" + UNREQUESTABLE_SAMPLE + "' and @disabled]")); @@ -345,7 +345,7 @@ private void createRequest() // add additional specimens clickAndWait(Locator.linkWithText("Specimen Data")); - waitAndClick(Locator.linkWithText("Vials by Derivative")); + waitAndClick(Locator.linkWithText("Vials by DerivativeType")); waitAndClick(WAIT_FOR_JAVASCRIPT, Locator.linkWithText("Swab"), WAIT_FOR_PAGE); checkCheckbox(".toggle"); clickMenuButtonAndContinue("Request Options", "Add To Existing Request"); @@ -612,7 +612,7 @@ private void verifyRequestCancel() waitForText("No data to show."); clickAndWait(Locator.linkWithText(getStudyLabel())); clickAndWait(Locator.linkWithText("Specimen Data")); - click(Locator.linkWithText("Vials by Derivative")); + click(Locator.linkWithText("Vials by DerivativeType")); waitAndClick(WAIT_FOR_JAVASCRIPT, Locator.linkWithText("Swab"), WAIT_FOR_PAGE); checkCheckbox(".toggle"); clickMenuButton("Request Options", "Create New Request"); From 0a6a35cc31067e152b70a762232d1ff9e62447cc Mon Sep 17 00:00:00 2001 From: Trey Chadick Date: Tue, 22 Jan 2013 18:22:23 +0000 Subject: [PATCH 083/494] Add test for disabled users in specimen requests SVN r24192 |2013-01-22 18:22:23 +0000 --- src/org/labkey/test/tests/SpecimenTest.java | 69 ++++++++++++++++++++- 1 file changed, 68 insertions(+), 1 deletion(-) diff --git a/src/org/labkey/test/tests/SpecimenTest.java b/src/org/labkey/test/tests/SpecimenTest.java index 2efdf903..4e53e988 100644 --- a/src/org/labkey/test/tests/SpecimenTest.java +++ b/src/org/labkey/test/tests/SpecimenTest.java @@ -20,10 +20,12 @@ import org.apache.http.HttpStatus; import org.junit.Assert; import org.labkey.test.Locator; +import org.labkey.test.TestTimeoutException; import org.labkey.test.WebTestHelper; import org.labkey.test.util.DataRegionTable; import org.labkey.test.util.LabKeyExpectedConditions; import org.labkey.test.util.LogMethod; +import org.labkey.test.util.LoggedParam; import org.labkey.test.util.PasswordUtil; import org.labkey.test.util.ext4cmp.Ext4FieldRefWD; @@ -71,6 +73,13 @@ protected boolean isFileUploadTest() return true; } + @Override + protected void doCleanup(boolean afterTest) throws TestTimeoutException + { + deleteUsers(afterTest, USER1, USER2); + super.doCleanup(afterTest); + } + @Override @LogMethod protected void doCreateSteps() { @@ -105,6 +114,7 @@ protected void doVerifySteps() verifyViews(); verifyAdditionalRequestFields(); verifyNotificationEmails(); + verifyInactiveUsersInRequests(); verifyRequestCancel(); verifyReports(); exportSpecimenTest(); @@ -604,6 +614,15 @@ private void verifyNotificationEmails() @LogMethod private void verifyRequestCancel() { + clickFolder(getProjectName()); + clickFolder(getFolderName()); + + waitAndClick(Locator.linkWithText("Specimen Requests")); + click(Locator.linkWithText("View Current Requests")); + + waitForElement(Locator.id("dataregion_SpecimenRequest")); + clickButton("Details"); + clickAndWait(Locator.linkWithText("Update Request")); selectOptionByText(Locator.name("status"), "Not Yet Submitted"); clickButton("Save Changes and Send Notifications"); @@ -728,12 +747,60 @@ private void verifySpecimenTableAttachments() assertElementPresent(Locator.css("#email_body_2 > table"), 1); } + @LogMethod + private void verifyInactiveUsersInRequests() + { + enableEmailRecorder(); // clear email recorder + goToSiteUsers(); + DataRegionTable usersTable = new DataRegionTable("Users", this, true, true); + int row = usersTable.getRow("Email", USER2); + usersTable.checkCheckbox(row); + clickButton("Deactivate"); + clickButton("Deactivate"); + assertTextNotPresent(USER2); + + clickFolder(getProjectName()); + clickFolder(getFolderName()); + + waitAndClick(Locator.linkWithText("Specimen Requests")); + click(Locator.linkWithText("View Current Requests")); + + waitForElement(Locator.id("dataregion_SpecimenRequest")); + clickButton("Details"); + + waitAndClick(Locator.linkWithText("Update Request")); + + waitForElement(Locator.pageHeader("Update Request")); + checkCheckbox(Locator.checkboxByName("notificationIdPairs")); + click(Locator.css(".labkey-help-pop-up")); + waitForElement(Locator.xpath("id('helpDivBody')").containing(USER1)); + + checkCheckbox(Locator.checkboxByName("notificationIdPairs").index(1)); + click(Locator.css(".labkey-help-pop-up").index(1)); + waitForElement(Locator.xpath("id('helpDivBody')/del").withText(USER2)); + + setFormElement(Locator.name("requestDescription"), "Just one notification."); + pushLocation(); + clickButton("Save Changes and Send Notifications"); + + popLocation(); + + waitForElement(Locator.pageHeader("Update Request")); + checkCheckbox(Locator.checkboxByName("notificationIdPairs")); + checkCheckbox(Locator.checkboxByName("notificationIdPairs").index(1)); + checkCheckbox(Locator.checkboxByName("emailInactiveUsers")); + setFormElement(Locator.name("requestDescription"), "Two notifications."); + clickButton("Save Changes and Send Notifications"); + + goToModule("Dumbster"); + } + /** * Allow all provided location types to make requests, disallow all others * @param types List of location types to allow to be requesting locations */ @LogMethod - private void enableAndVerifyRequestingLocationTypes(StudyLocationType... types) + private void enableAndVerifyRequestingLocationTypes(@LoggedParam StudyLocationType... types) { clickTab("Manage"); waitAndClick(Locator.linkWithText("Manage Location Types")); From 58d290861a6325afc2b156afbf87e79869674c99 Mon Sep 17 00:00:00 2001 From: Dave Bradlee Date: Mon, 28 Jan 2013 23:44:06 +0000 Subject: [PATCH 084/494] Configure Specimen Web Part automated test SVN r24321 |2013-01-28 23:44:06 +0000 --- src/org/labkey/test/tests/SpecimenTest.java | 39 +++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src/org/labkey/test/tests/SpecimenTest.java b/src/org/labkey/test/tests/SpecimenTest.java index 4e53e988..3cf6ee07 100644 --- a/src/org/labkey/test/tests/SpecimenTest.java +++ b/src/org/labkey/test/tests/SpecimenTest.java @@ -121,6 +121,7 @@ protected void doVerifySteps() verifyRequestingLocationRestriction(); verifySpecimenTableAttachments(); searchTest(); + verifySpecimenGroupings(); } @LogMethod @@ -851,6 +852,44 @@ private void verifyRequestingLocationCounts(StudyLocationType... types) waitForElement(Locator.id("labkey-nav-trail-current-page").withText("Specimen Requests")); } + /** + * Verify changing the specimen groupings that appear on the specimen web part + */ + @LogMethod + private void verifySpecimenGroupings() + { + clickFolder(getProjectName()); + clickFolder(getFolderName()); + clickTab("Manage"); + waitAndClick(Locator.linkWithText("Configure Specimen Groupings")); + waitForElement(Locator.id("labkey-nav-trail-current-page").withText("Configure Specimen Web Part")); + _ext4Helper.selectComboBoxItemById("combo11", "ProcessingLocation"); + _ext4Helper.selectComboBoxItemById("combo12", "PrimaryType"); + _ext4Helper.selectComboBoxItemById("combo13", "SiteName"); + _ext4Helper.selectComboBoxItemById("combo21", "AdditiveType"); + _ext4Helper.selectComboBoxItemById("combo22", "DerivativeType"); + _ext4Helper.selectComboBoxItemById("combo23", "TubeType"); + clickButton("Save"); + waitForElement(Locator.id("labkey-nav-trail-current-page").withText("Manage Study")); + clickTab("Specimen Data"); + assertTextPresent("Vials by ProcessingLocation", "Vials by AdditiveType", "The McMichael Lab"); + assertTextPresent("NICD - Joberg", 2); + clickAndWait(Locator.linkContainingText("The McMichael Lab, Oxford")); + assertTextPresent("Vials", "(ProcessingLocation = The McMichael Lab, Oxford, UK)"); + + // Put groupings back for other tests + clickTab("Manage"); + waitAndClick(Locator.linkWithText("Configure Specimen Groupings")); + waitForElement(Locator.id("labkey-nav-trail-current-page").withText("Configure Specimen Web Part")); + _ext4Helper.selectComboBoxItemById("combo11", "PrimaryType"); + _ext4Helper.selectComboBoxItemById("combo12", "DerivativeType"); + _ext4Helper.selectComboBoxItemById("combo13", "AdditiveType"); + _ext4Helper.selectComboBoxItemById("combo21", "DerivativeType"); + _ext4Helper.selectComboBoxItemById("combo22", "AdditiveType"); + + clickButton("Save"); + } + /** * Provides info about locations defined in sample_a.specimens */ From c7f01dc52ae25dbdc99f2fb19353b718b06eedd8 Mon Sep 17 00:00:00 2001 From: Dave Bradlee Date: Thu, 31 Jan 2013 21:59:47 +0000 Subject: [PATCH 085/494] Bug 17056: Nicer names for specimen web part SVN r24409 |2013-01-31 21:59:47 +0000 --- src/org/labkey/test/tests/SpecimenTest.java | 32 ++++++++++----------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/org/labkey/test/tests/SpecimenTest.java b/src/org/labkey/test/tests/SpecimenTest.java index 3cf6ee07..7fde5a14 100644 --- a/src/org/labkey/test/tests/SpecimenTest.java +++ b/src/org/labkey/test/tests/SpecimenTest.java @@ -182,7 +182,7 @@ private void checkTubeType() assertTextPresent("20ml Cryovial"); assertTextPresent("25ml Cryovial"); clickAndWait(Locator.linkWithText("Specimen Overview")); - click(Locator.linkWithText("Vials by DerivativeType")); + click(Locator.linkWithText("Vials by Derivative Type")); waitAndClick(WAIT_FOR_JAVASCRIPT, Locator.linkWithText("Tear Flo Strips"), WAIT_FOR_PAGE); // For these three vials, there should be no conflict in TubeType, so we should see the text once for each of three vials: assertLinkPresentWithTextCount("[history]", 3); @@ -315,7 +315,7 @@ private void createRequest() { clickAndWait(Locator.linkWithText(getStudyLabel())); clickAndWait(Locator.linkWithText("Specimen Data")); - click(Locator.linkWithText("Vials by DerivativeType")); + click(Locator.linkWithText("Vials by Derivative Type")); waitAndClick(WAIT_FOR_JAVASCRIPT, Locator.linkWithText("Plasma, Unknown Processing"), WAIT_FOR_PAGE); // Verify unavailable sample assertElementPresent(Locator.xpath("//input[@id='check_" + UNREQUESTABLE_SAMPLE + "' and @disabled]")); @@ -356,7 +356,7 @@ private void createRequest() // add additional specimens clickAndWait(Locator.linkWithText("Specimen Data")); - waitAndClick(Locator.linkWithText("Vials by DerivativeType")); + waitAndClick(Locator.linkWithText("Vials by Derivative Type")); waitAndClick(WAIT_FOR_JAVASCRIPT, Locator.linkWithText("Swab"), WAIT_FOR_PAGE); checkCheckbox(".toggle"); clickMenuButtonAndContinue("Request Options", "Add To Existing Request"); @@ -632,7 +632,7 @@ private void verifyRequestCancel() waitForText("No data to show."); clickAndWait(Locator.linkWithText(getStudyLabel())); clickAndWait(Locator.linkWithText("Specimen Data")); - click(Locator.linkWithText("Vials by DerivativeType")); + click(Locator.linkWithText("Vials by Derivative Type")); waitAndClick(WAIT_FOR_JAVASCRIPT, Locator.linkWithText("Swab"), WAIT_FOR_PAGE); checkCheckbox(".toggle"); clickMenuButton("Request Options", "Create New Request"); @@ -863,16 +863,16 @@ private void verifySpecimenGroupings() clickTab("Manage"); waitAndClick(Locator.linkWithText("Configure Specimen Groupings")); waitForElement(Locator.id("labkey-nav-trail-current-page").withText("Configure Specimen Web Part")); - _ext4Helper.selectComboBoxItemById("combo11", "ProcessingLocation"); - _ext4Helper.selectComboBoxItemById("combo12", "PrimaryType"); - _ext4Helper.selectComboBoxItemById("combo13", "SiteName"); - _ext4Helper.selectComboBoxItemById("combo21", "AdditiveType"); - _ext4Helper.selectComboBoxItemById("combo22", "DerivativeType"); - _ext4Helper.selectComboBoxItemById("combo23", "TubeType"); + _ext4Helper.selectComboBoxItemById("combo11", "Processing Location"); + _ext4Helper.selectComboBoxItemById("combo12", "Primary Type"); + _ext4Helper.selectComboBoxItemById("combo13", "Site Name"); + _ext4Helper.selectComboBoxItemById("combo21", "Additive Type"); + _ext4Helper.selectComboBoxItemById("combo22", "Derivative Type"); + _ext4Helper.selectComboBoxItemById("combo23", "Tube Type"); clickButton("Save"); waitForElement(Locator.id("labkey-nav-trail-current-page").withText("Manage Study")); clickTab("Specimen Data"); - assertTextPresent("Vials by ProcessingLocation", "Vials by AdditiveType", "The McMichael Lab"); + assertTextPresent("Vials by Processing Location", "Vials by Additive Type", "The McMichael Lab"); assertTextPresent("NICD - Joberg", 2); clickAndWait(Locator.linkContainingText("The McMichael Lab, Oxford")); assertTextPresent("Vials", "(ProcessingLocation = The McMichael Lab, Oxford, UK)"); @@ -881,11 +881,11 @@ private void verifySpecimenGroupings() clickTab("Manage"); waitAndClick(Locator.linkWithText("Configure Specimen Groupings")); waitForElement(Locator.id("labkey-nav-trail-current-page").withText("Configure Specimen Web Part")); - _ext4Helper.selectComboBoxItemById("combo11", "PrimaryType"); - _ext4Helper.selectComboBoxItemById("combo12", "DerivativeType"); - _ext4Helper.selectComboBoxItemById("combo13", "AdditiveType"); - _ext4Helper.selectComboBoxItemById("combo21", "DerivativeType"); - _ext4Helper.selectComboBoxItemById("combo22", "AdditiveType"); + _ext4Helper.selectComboBoxItemById("combo11", "Primary Type"); + _ext4Helper.selectComboBoxItemById("combo12", "Derivative Type"); + _ext4Helper.selectComboBoxItemById("combo13", "Additive Type"); + _ext4Helper.selectComboBoxItemById("combo21", "Derivative Type"); + _ext4Helper.selectComboBoxItemById("combo22", "Additive Type"); clickButton("Save"); } From 1d666368a2e10a3a4130f31e2cc4a18373febdf3 Mon Sep 17 00:00:00 2001 From: Trey Chadick Date: Wed, 13 Feb 2013 17:28:01 +0000 Subject: [PATCH 086/494] Try to prevent WebDriver socket timeout SVN r24658 |2013-02-13 17:28:01 +0000 --- src/org/labkey/test/tests/SpecimenTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/org/labkey/test/tests/SpecimenTest.java b/src/org/labkey/test/tests/SpecimenTest.java index 7fde5a14..ca4e2594 100644 --- a/src/org/labkey/test/tests/SpecimenTest.java +++ b/src/org/labkey/test/tests/SpecimenTest.java @@ -571,7 +571,7 @@ private void verifyNotificationEmails() Assert.assertTrue(!bodyText.contains(_specimen_KCMC)); click(Locator.linkContainingText("Specimen Request Notification").index(1)); _shortWait.until(LabKeyExpectedConditions.emailIsExpanded(2)); - bodyText = getBodyText(); + bodyText = getText(Locator.id("dataregion_EmailRecord")); Assert.assertTrue(bodyText.contains(_specimen_KCMC)); DataRegionTable mailTable = new DataRegionTable("EmailRecord", this, false, false); Assert.assertEquals("Notification was not as expected", notification, mailTable.getDataAsText(1, "Message")); @@ -585,7 +585,7 @@ private void verifyNotificationEmails() Assert.assertTrue(!bodyText.contains(_specimen_McMichael)); click(Locator.linkContainingText("Specimen Request Notification").index(1)); _shortWait.until(LabKeyExpectedConditions.emailIsExpanded(2)); - bodyText = getBodyText(); + bodyText = getText(Locator.id("dataregion_EmailRecord")); Assert.assertTrue(bodyText.contains(_specimen_McMichael)); DataRegionTable mailTable = new DataRegionTable("EmailRecord", this, false, false); Assert.assertEquals("Notification was not as expected", notification, mailTable.getDataAsText(0, "Message")); From e3e2083ef1741aa6fd7286115fefad43c3d04c99 Mon Sep 17 00:00:00 2001 From: Trey Chadick Date: Fri, 22 Feb 2013 18:11:57 +0000 Subject: [PATCH 087/494] Try to avoid intermittent WebDriver timeout SVN r24802 |2013-02-22 18:11:57 +0000 --- src/org/labkey/test/tests/SpecimenTest.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/org/labkey/test/tests/SpecimenTest.java b/src/org/labkey/test/tests/SpecimenTest.java index ca4e2594..a1381eb9 100644 --- a/src/org/labkey/test/tests/SpecimenTest.java +++ b/src/org/labkey/test/tests/SpecimenTest.java @@ -571,6 +571,7 @@ private void verifyNotificationEmails() Assert.assertTrue(!bodyText.contains(_specimen_KCMC)); click(Locator.linkContainingText("Specimen Request Notification").index(1)); _shortWait.until(LabKeyExpectedConditions.emailIsExpanded(2)); + sleep(100); // Avoid WebDriver timeout bodyText = getText(Locator.id("dataregion_EmailRecord")); Assert.assertTrue(bodyText.contains(_specimen_KCMC)); DataRegionTable mailTable = new DataRegionTable("EmailRecord", this, false, false); @@ -585,6 +586,7 @@ private void verifyNotificationEmails() Assert.assertTrue(!bodyText.contains(_specimen_McMichael)); click(Locator.linkContainingText("Specimen Request Notification").index(1)); _shortWait.until(LabKeyExpectedConditions.emailIsExpanded(2)); + sleep(100); // Avoid WebDriver timeout bodyText = getText(Locator.id("dataregion_EmailRecord")); Assert.assertTrue(bodyText.contains(_specimen_McMichael)); DataRegionTable mailTable = new DataRegionTable("EmailRecord", this, false, false); From 0e7918949a865d9759a896b67451a97b3b48b0a7 Mon Sep 17 00:00:00 2001 From: Trey Chadick Date: Mon, 25 Feb 2013 17:54:09 +0000 Subject: [PATCH 088/494] Wait for specimen webpart to render SVN r24838 |2013-02-25 17:54:09 +0000 --- src/org/labkey/test/tests/SpecimenTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/org/labkey/test/tests/SpecimenTest.java b/src/org/labkey/test/tests/SpecimenTest.java index a1381eb9..96c3f136 100644 --- a/src/org/labkey/test/tests/SpecimenTest.java +++ b/src/org/labkey/test/tests/SpecimenTest.java @@ -874,6 +874,7 @@ private void verifySpecimenGroupings() clickButton("Save"); waitForElement(Locator.id("labkey-nav-trail-current-page").withText("Manage Study")); clickTab("Specimen Data"); + waitForElement(Locator.linkWithText("Vials by Processing Location")); assertTextPresent("Vials by Processing Location", "Vials by Additive Type", "The McMichael Lab"); assertTextPresent("NICD - Joberg", 2); clickAndWait(Locator.linkContainingText("The McMichael Lab, Oxford")); From d461befecb02651ff95899615a94ff98ec2c792d Mon Sep 17 00:00:00 2001 From: Trey Chadick Date: Tue, 26 Feb 2013 16:55:18 +0000 Subject: [PATCH 089/494] Longer pause for expanding email SVN r24860 |2013-02-26 16:55:18 +0000 --- src/org/labkey/test/tests/SpecimenTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/org/labkey/test/tests/SpecimenTest.java b/src/org/labkey/test/tests/SpecimenTest.java index 96c3f136..210a2c2f 100644 --- a/src/org/labkey/test/tests/SpecimenTest.java +++ b/src/org/labkey/test/tests/SpecimenTest.java @@ -571,7 +571,7 @@ private void verifyNotificationEmails() Assert.assertTrue(!bodyText.contains(_specimen_KCMC)); click(Locator.linkContainingText("Specimen Request Notification").index(1)); _shortWait.until(LabKeyExpectedConditions.emailIsExpanded(2)); - sleep(100); // Avoid WebDriver timeout + sleep(500); // Avoid WebDriver timeout bodyText = getText(Locator.id("dataregion_EmailRecord")); Assert.assertTrue(bodyText.contains(_specimen_KCMC)); DataRegionTable mailTable = new DataRegionTable("EmailRecord", this, false, false); @@ -586,7 +586,7 @@ private void verifyNotificationEmails() Assert.assertTrue(!bodyText.contains(_specimen_McMichael)); click(Locator.linkContainingText("Specimen Request Notification").index(1)); _shortWait.until(LabKeyExpectedConditions.emailIsExpanded(2)); - sleep(100); // Avoid WebDriver timeout + sleep(500); // Avoid WebDriver timeout bodyText = getText(Locator.id("dataregion_EmailRecord")); Assert.assertTrue(bodyText.contains(_specimen_McMichael)); DataRegionTable mailTable = new DataRegionTable("EmailRecord", this, false, false); From 663fa5ccce8fdf96b5657e33e54b5b4050de3019 Mon Sep 17 00:00:00 2001 From: Elizabeth Vannostrand Date: Fri, 8 Mar 2013 23:24:20 +0000 Subject: [PATCH 090/494] SpecimenReplaceTest SVN r25038 |2013-03-08 23:24:20 +0000 --- src/org/labkey/test/tests/SpecimenTest.java | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/org/labkey/test/tests/SpecimenTest.java b/src/org/labkey/test/tests/SpecimenTest.java index 210a2c2f..b2a5dfd2 100644 --- a/src/org/labkey/test/tests/SpecimenTest.java +++ b/src/org/labkey/test/tests/SpecimenTest.java @@ -80,7 +80,8 @@ protected void doCleanup(boolean afterTest) throws TestTimeoutException super.doCleanup(afterTest); } - @Override @LogMethod + @Override + @LogMethod(category = LogMethod.MethodType.SETUP) protected void doCreateSteps() { enableEmailRecorder(); @@ -106,16 +107,17 @@ protected void doCreateSteps() uploadSpecimensFromFile(); } - @Override @LogMethod + @Override + @LogMethod(category = LogMethod.MethodType.VERIFICATION) protected void doVerifySteps() { - verifyActorDetails(); - createRequest(); - verifyViews(); - verifyAdditionalRequestFields(); - verifyNotificationEmails(); - verifyInactiveUsersInRequests(); - verifyRequestCancel(); +// verifyActorDetails(); +// createRequest(); +// verifyViews(); +// verifyAdditionalRequestFields(); +// verifyNotificationEmails(); +// verifyInactiveUsersInRequests(); +// verifyRequestCancel(); verifyReports(); exportSpecimenTest(); verifyRequestingLocationRestriction(); From 1f7dbdc731241c9799111fedbe9e21fc527b0032 Mon Sep 17 00:00:00 2001 From: Elizabeth Vannostrand Date: Mon, 11 Mar 2013 22:36:39 +0000 Subject: [PATCH 091/494] I commented out part of specimen test in prep for splitting it, but didn't get around to it. This reverts that change. SVN r25075 |2013-03-11 22:36:39 +0000 --- src/org/labkey/test/tests/SpecimenTest.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/org/labkey/test/tests/SpecimenTest.java b/src/org/labkey/test/tests/SpecimenTest.java index b2a5dfd2..73cc940e 100644 --- a/src/org/labkey/test/tests/SpecimenTest.java +++ b/src/org/labkey/test/tests/SpecimenTest.java @@ -111,13 +111,13 @@ protected void doCreateSteps() @LogMethod(category = LogMethod.MethodType.VERIFICATION) protected void doVerifySteps() { -// verifyActorDetails(); -// createRequest(); -// verifyViews(); -// verifyAdditionalRequestFields(); -// verifyNotificationEmails(); -// verifyInactiveUsersInRequests(); -// verifyRequestCancel(); + verifyActorDetails(); + createRequest(); + verifyViews(); + verifyAdditionalRequestFields(); + verifyNotificationEmails(); + verifyInactiveUsersInRequests(); + verifyRequestCancel(); verifyReports(); exportSpecimenTest(); verifyRequestingLocationRestriction(); From 18c9c9099a3903a3094d302dc324187e81b1b203 Mon Sep 17 00:00:00 2001 From: Trey Chadick Date: Wed, 20 Mar 2013 16:19:29 +0000 Subject: [PATCH 092/494] Narrower getText target for email verification SVN r25267 |2013-03-20 16:19:29 +0000 --- src/org/labkey/test/tests/SpecimenTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/org/labkey/test/tests/SpecimenTest.java b/src/org/labkey/test/tests/SpecimenTest.java index 73cc940e..a7820a66 100644 --- a/src/org/labkey/test/tests/SpecimenTest.java +++ b/src/org/labkey/test/tests/SpecimenTest.java @@ -568,7 +568,7 @@ private void verifyNotificationEmails() { click(Locator.linkContainingText("Specimen Request Notification")); _shortWait.until(LabKeyExpectedConditions.emailIsExpanded(1)); - String bodyText = getBodyText(); + String bodyText = getText(Locator.id("dataregion_EmailRecord")); Assert.assertTrue(bodyText.contains(_specimen_McMichael)); Assert.assertTrue(!bodyText.contains(_specimen_KCMC)); click(Locator.linkContainingText("Specimen Request Notification").index(1)); @@ -583,7 +583,7 @@ private void verifyNotificationEmails() { click(Locator.linkContainingText("Specimen Request Notification")); _shortWait.until(LabKeyExpectedConditions.emailIsExpanded(1)); - String bodyText = getBodyText(); + String bodyText = getText(Locator.id("dataregion_EmailRecord")); Assert.assertTrue(bodyText.contains(_specimen_KCMC)); Assert.assertTrue(!bodyText.contains(_specimen_McMichael)); click(Locator.linkContainingText("Specimen Request Notification").index(1)); From 152f64d2537c234529d3578b6cdd909459ed4148 Mon Sep 17 00:00:00 2001 From: Trey Chadick Date: Wed, 27 Mar 2013 20:59:21 +0000 Subject: [PATCH 093/494] Try to avoid WebDriver timeout by simplifying email check SVN r25462 |2013-03-27 20:59:21 +0000 --- src/org/labkey/test/tests/SpecimenTest.java | 38 +++++---------------- 1 file changed, 8 insertions(+), 30 deletions(-) diff --git a/src/org/labkey/test/tests/SpecimenTest.java b/src/org/labkey/test/tests/SpecimenTest.java index a7820a66..3f2fb988 100644 --- a/src/org/labkey/test/tests/SpecimenTest.java +++ b/src/org/labkey/test/tests/SpecimenTest.java @@ -564,36 +564,14 @@ private void verifyNotificationEmails() assertTextPresent(USER2, 4); log("Check for correct data in notification emails"); - if (getTableCellText("dataregion_EmailRecord", 2, 0).equals(USER1)) - { - click(Locator.linkContainingText("Specimen Request Notification")); - _shortWait.until(LabKeyExpectedConditions.emailIsExpanded(1)); - String bodyText = getText(Locator.id("dataregion_EmailRecord")); - Assert.assertTrue(bodyText.contains(_specimen_McMichael)); - Assert.assertTrue(!bodyText.contains(_specimen_KCMC)); - click(Locator.linkContainingText("Specimen Request Notification").index(1)); - _shortWait.until(LabKeyExpectedConditions.emailIsExpanded(2)); - sleep(500); // Avoid WebDriver timeout - bodyText = getText(Locator.id("dataregion_EmailRecord")); - Assert.assertTrue(bodyText.contains(_specimen_KCMC)); - DataRegionTable mailTable = new DataRegionTable("EmailRecord", this, false, false); - Assert.assertEquals("Notification was not as expected", notification, mailTable.getDataAsText(1, "Message")); - } - else - { - click(Locator.linkContainingText("Specimen Request Notification")); - _shortWait.until(LabKeyExpectedConditions.emailIsExpanded(1)); - String bodyText = getText(Locator.id("dataregion_EmailRecord")); - Assert.assertTrue(bodyText.contains(_specimen_KCMC)); - Assert.assertTrue(!bodyText.contains(_specimen_McMichael)); - click(Locator.linkContainingText("Specimen Request Notification").index(1)); - _shortWait.until(LabKeyExpectedConditions.emailIsExpanded(2)); - sleep(500); // Avoid WebDriver timeout - bodyText = getText(Locator.id("dataregion_EmailRecord")); - Assert.assertTrue(bodyText.contains(_specimen_McMichael)); - DataRegionTable mailTable = new DataRegionTable("EmailRecord", this, false, false); - Assert.assertEquals("Notification was not as expected", notification, mailTable.getDataAsText(0, "Message")); - } + int emailIndex = getTableCellText(Locator.id("dataregion_EmailRecord"), 2, 0).equals(USER1) ? 1 : 0; + click(Locator.linkContainingText("Specimen Request Notification").index(emailIndex)); + _shortWait.until(LabKeyExpectedConditions.emailIsExpanded(emailIndex + 1)); + String bodyText = getText(Locator.id("dataregion_EmailRecord")); + Assert.assertTrue(!bodyText.contains(_specimen_McMichael)); + Assert.assertTrue(bodyText.contains(_specimen_KCMC)); + DataRegionTable mailTable = new DataRegionTable("EmailRecord", this, false, false); + Assert.assertEquals("Notification was not as expected", notification, mailTable.getDataAsText(emailIndex, "Message")); String attachment1 = getAttribute(Locator.linkWithText(ATTACHMENT1), "href"); String attachment2 = getAttribute(Locator.linkWithText(String.format(ATTACHMENT2, date)), "href"); From 797214f3ea41cd2dc2994eb9a3bcd07d58415f00 Mon Sep 17 00:00:00 2001 From: Trey Chadick Date: Thu, 18 Apr 2013 21:02:00 +0000 Subject: [PATCH 094/494] Update tests to not use left nav bar SVN r25820 |2013-04-18 21:02:00 +0000 --- src/org/labkey/test/tests/SpecimenTest.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/org/labkey/test/tests/SpecimenTest.java b/src/org/labkey/test/tests/SpecimenTest.java index 3f2fb988..307d7686 100644 --- a/src/org/labkey/test/tests/SpecimenTest.java +++ b/src/org/labkey/test/tests/SpecimenTest.java @@ -143,7 +143,7 @@ private void setupRequestabilityRules() clickButton("Save", 0); waitForText("Saved", WAIT_FOR_JAVASCRIPT); - clickAndWait(Locator.linkWithText(getFolderName())); + clickFolder(getFolderName()); waitAndClick(Locator.linkWithText("Manage Study")); waitAndClick(Locator.linkWithText("Manage Requestability Rules")); // Verify that LOCKED_IN_REQUEST is the last rule @@ -597,7 +597,7 @@ private void verifyNotificationEmails() @LogMethod private void verifyRequestCancel() { - clickFolder(getProjectName()); + clickProject(getProjectName()); clickFolder(getFolderName()); waitAndClick(Locator.linkWithText("Specimen Requests")); @@ -655,7 +655,7 @@ private void verifyReports() @LogMethod private void verifyRequestingLocationRestriction() { - clickFolder(getProjectName()); + clickProject(getProjectName()); clickFolder(getFolderName()); verifyRequestingLocationCounts(StudyLocationType.values()); // All locations should be enabled by default @@ -672,7 +672,7 @@ private void verifyRequestingLocationRestriction() @LogMethod private void verifySpecimenTableAttachments() { - clickFolder(getProjectName()); + clickProject(getProjectName()); clickFolder(getFolderName()); log("Setup Excel specimen attachment"); @@ -742,7 +742,7 @@ private void verifyInactiveUsersInRequests() clickButton("Deactivate"); assertTextNotPresent(USER2); - clickFolder(getProjectName()); + clickProject(getProjectName()); clickFolder(getFolderName()); waitAndClick(Locator.linkWithText("Specimen Requests")); @@ -840,7 +840,7 @@ private void verifyRequestingLocationCounts(StudyLocationType... types) @LogMethod private void verifySpecimenGroupings() { - clickFolder(getProjectName()); + clickProject(getProjectName()); clickFolder(getFolderName()); clickTab("Manage"); waitAndClick(Locator.linkWithText("Configure Specimen Groupings")); From 9e1d90363e34bc3b3b43e97a84e77202a7a6f7f3 Mon Sep 17 00:00:00 2001 From: Trey Chadick Date: Fri, 19 Apr 2013 17:30:44 +0000 Subject: [PATCH 095/494] Use clickFolder to navigate to study containers SVN r25835 |2013-04-19 17:30:44 +0000 --- src/org/labkey/test/tests/SpecimenTest.java | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/org/labkey/test/tests/SpecimenTest.java b/src/org/labkey/test/tests/SpecimenTest.java index 307d7686..0602672e 100644 --- a/src/org/labkey/test/tests/SpecimenTest.java +++ b/src/org/labkey/test/tests/SpecimenTest.java @@ -170,7 +170,7 @@ private void setupRequestabilityRules() private void checkTubeType() { // Field check for Tube Type column (including conflict) - clickAndWait(Locator.linkWithText(getStudyLabel())); + clickFolder(getStudyLabel()); addWebPart("Specimens"); clickAndWait(Locator.linkWithText("By Individual Vial")); setFilter(SPECIMEN_DETAIL, "PrimaryType", "Is Blank"); @@ -210,7 +210,7 @@ private void setupActorsAndGroups() setFormElement(Locator.name("names"), USER2); uncheckCheckbox("sendEmail"); clickAndWait(Locator.linkWithText("Update Members")); - clickAndWait(Locator.linkWithText(getStudyLabel())); + clickFolder(getStudyLabel()); } @LogMethod (quiet = true) @@ -242,14 +242,14 @@ private void setupRequestForm() setFormElement(Locator.xpath("//descendant::input[@name='helpText'][4]"), "A test input"); click(Locator.xpath("//descendant::input[@name='required'][4]")); clickButton("Save"); - clickAndWait(Locator.linkWithText(getStudyLabel())); + clickFolder(getStudyLabel()); } @LogMethod private void setupActorNotification() { log("Check Configure Defaults for Actor Notification"); - clickAndWait(Locator.linkWithText(getStudyLabel())); + clickFolder(getStudyLabel()); clickAndWait(Locator.linkWithText("Manage")); clickAndWait(Locator.linkWithText("Manage Notifications")); assertTextPresent("Default Email Recipients"); @@ -261,7 +261,7 @@ private void setupActorNotification() private void uploadSpecimensFromFile() { log("Check Upload Specimen List from file"); - clickAndWait(Locator.linkWithText(getStudyLabel())); + clickFolder(getStudyLabel()); clickAndWait(Locator.linkWithText("Specimen Data")); waitAndClick(Locator.linkWithText("Specimen Requests")); // expand node in Specimens webpart clickAndWait(Locator.linkWithText("Create New Request")); @@ -315,7 +315,7 @@ private void verifyActorDetails() @LogMethod private void createRequest() { - clickAndWait(Locator.linkWithText(getStudyLabel())); + clickFolder(getStudyLabel()); clickAndWait(Locator.linkWithText("Specimen Data")); click(Locator.linkWithText("Vials by Derivative Type")); waitAndClick(WAIT_FOR_JAVASCRIPT, Locator.linkWithText("Plasma, Unknown Processing"), WAIT_FOR_PAGE); @@ -354,7 +354,7 @@ private void createRequest() // verify that the swab specimen isn't present yet assertTextNotPresent("DAA07YGW-01"); assertTextNotPresent("Complete"); - clickAndWait(Locator.linkWithText(getStudyLabel())); + clickFolder(getStudyLabel()); // add additional specimens clickAndWait(Locator.linkWithText("Specimen Data")); @@ -475,7 +475,7 @@ private void verifyAdditionalRequestFields() popLocation(); log("verifying column changes"); - clickAndWait(Locator.linkWithText(getStudyLabel())); + clickFolder(getStudyLabel()); clickAndWait(Locator.linkWithText("Specimen Data")); waitAndClick(Locator.linkWithText("Specimen Requests")); // expand node in Specimens webpart clickAndWait(Locator.xpath("//a[text() = 'View Current Requests']")); @@ -612,7 +612,7 @@ private void verifyRequestCancel() clickButton("Cancel Request", 0); assertAlert("Canceling will permanently delete this pending request. Continue?"); waitForText("No data to show."); - clickAndWait(Locator.linkWithText(getStudyLabel())); + clickFolder(getStudyLabel()); clickAndWait(Locator.linkWithText("Specimen Data")); click(Locator.linkWithText("Vials by Derivative Type")); waitAndClick(WAIT_FOR_JAVASCRIPT, Locator.linkWithText("Swab"), WAIT_FOR_PAGE); From 65666e9c647489043d7b4652a1291a7805e1ffe1 Mon Sep 17 00:00:00 2001 From: Trey Chadick Date: Fri, 19 Apr 2013 21:22:22 +0000 Subject: [PATCH 096/494] More folder bar fixes SVN r25843 |2013-04-19 21:22:22 +0000 --- src/org/labkey/test/tests/SpecimenTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/org/labkey/test/tests/SpecimenTest.java b/src/org/labkey/test/tests/SpecimenTest.java index 0602672e..4f9c9b4d 100644 --- a/src/org/labkey/test/tests/SpecimenTest.java +++ b/src/org/labkey/test/tests/SpecimenTest.java @@ -912,7 +912,7 @@ public static int untypedSites() private void searchTest() { goToProjectHome(); - clickAndWait(Locator.linkContainingText(getFolderName())); + clickFolder(getFolderName()); clickTab("Specimen Data"); waitForVialSearch(); Ext4FieldRefWD additiveType = Ext4FieldRefWD.getForLabel(this, "Additive Type"); From 3efebb976354e28646c7e143833a934b7c35a8f9 Mon Sep 17 00:00:00 2001 From: Cory Nathe Date: Tue, 23 Apr 2013 14:03:39 +0000 Subject: [PATCH 097/494] Dataset label to name conversion (including patches from Dave and Adam) - one time migration of existing reports, custom views, and query snapshots after upgrade by calling QueryChangeListeners - fix report, custom view, and query snapshot implementations to use dataset name instead of label - update DatasetAction to use name to retrieve associated reports, views, and query snapshots - update dataset retrieving methods in StudyManager: get by name, lable, id, and queryname (checks both name and label) - change dataset tableInfo name to use dataset name instead of label - show dataset name and label in schema browser (and use name in generated SQL) - fixup several affected tests that were selecting datasets by label Implement QueryChangeListener.queryChanged for reports, custom views, and query snapshots - listeners called on query name change, dataset name change, and list name change - fixup query name references in reports (including JSON property for time chart, generic charts, and ptid reports) - fixup base query name reference in query snapshot definition - fixup query name references in custom views column list, filter, sort, and aggregate properties - use new limited user (ReferenceFixupUser) to save changes: NOTE: this will update modified and modifiedBy - NOTE: this will temporarily break private custom views that are associated with a dataset that has a different label than name SVN r25882 |2013-04-23 14:03:39 +0000 --- src/org/labkey/test/tests/SpecimenTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/org/labkey/test/tests/SpecimenTest.java b/src/org/labkey/test/tests/SpecimenTest.java index 4f9c9b4d..3d04fa4b 100644 --- a/src/org/labkey/test/tests/SpecimenTest.java +++ b/src/org/labkey/test/tests/SpecimenTest.java @@ -172,7 +172,7 @@ private void checkTubeType() // Field check for Tube Type column (including conflict) clickFolder(getStudyLabel()); addWebPart("Specimens"); - clickAndWait(Locator.linkWithText("By Individual Vial")); + waitAndClickAndWait(Locator.linkWithText("By Individual Vial")); setFilter(SPECIMEN_DETAIL, "PrimaryType", "Is Blank"); // Verify that there's only one vial of unknown type: assertLinkPresentWithTextCount("[history]", 1); From 11864d678eca7f72a486a2dfeb73c32249e96d41 Mon Sep 17 00:00:00 2001 From: Britt Piehler Date: Wed, 1 May 2013 16:21:38 +0000 Subject: [PATCH 098/494] Ability to enable/disable specimen request functionality when the advanced specimen repository is enabled. Includes automated tests and a tweak to the test harness to fix timing issues hit on my slower machine. SVN r26057 |2013-05-01 16:21:38 +0000 --- src/org/labkey/test/tests/SpecimenTest.java | 41 +++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/src/org/labkey/test/tests/SpecimenTest.java b/src/org/labkey/test/tests/SpecimenTest.java index 3d04fa4b..1e607267 100644 --- a/src/org/labkey/test/tests/SpecimenTest.java +++ b/src/org/labkey/test/tests/SpecimenTest.java @@ -124,6 +124,47 @@ protected void doVerifySteps() verifySpecimenTableAttachments(); searchTest(); verifySpecimenGroupings(); + verifyRequestEnabled(); + disableRequests(); + verifyRequestsDisabled(); + } + + private void disableRequests() + { + clickFolder(getFolderName()); + assertTextPresent("Specimen Requests"); + waitAndClick(Locator.linkWithText("Manage Study")); + waitAndClick(Locator.linkWithText("Change Repository Type")); + Locator disableRequestsRadio = Locator.radioButtonByNameAndValue("enableRequests", "false"); + waitForElement(disableRequestsRadio); + checkRadioButton(disableRequestsRadio); + clickButton("Submit"); + } + + private void verifyRequestEnabled() + { + clickFolder(getFolderName()); + assertTextPresent("Specimen Requests"); + click(Locator.linkWithText("By Individual Vial")); + assertElementPresent(Locator.navButton("Request Options")); + assertTextPresent("Locked In Request", "Requestable", "Available", "Availability Reason", "Locked In Request Count", "Available Count", "Expected Available Count"); + click(Locator.linkWithText("Reports")); + assertTextPresent("Requested Vials by Type and Timepoint", "Request Summary"); + clickButton("View"); + assertTextPresent("Availability status"); + } + + private void verifyRequestsDisabled() + { + clickFolder(getFolderName()); + assertTextNotPresent("Specimen Requests"); + click(Locator.linkWithText("By Individual Vial")); + assertElementNotPresent(Locator.navButton("Request Options")); + assertTextNotPresent("Locked In Request", "Requestable", "Available", "Availability Reason", "Locked In Request Count", "Available Count", "Expected Available Count"); + click(Locator.linkWithText("Reports")); + assertTextNotPresent("Requested Vials by Type and Timepoint", "Request Summary"); + clickButton("View"); + assertTextNotPresent("Availability status"); } @LogMethod From 0bd88785b3dcba17be916c22b5f670bdfaaa608b Mon Sep 17 00:00:00 2001 From: Trey Chadick Date: Fri, 3 May 2013 16:40:10 +0000 Subject: [PATCH 099/494] Wait for page loads SVN r26246 |2013-05-03 16:40:10 +0000 --- src/org/labkey/test/tests/SpecimenTest.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/org/labkey/test/tests/SpecimenTest.java b/src/org/labkey/test/tests/SpecimenTest.java index 1e607267..79e56c47 100644 --- a/src/org/labkey/test/tests/SpecimenTest.java +++ b/src/org/labkey/test/tests/SpecimenTest.java @@ -145,10 +145,10 @@ private void verifyRequestEnabled() { clickFolder(getFolderName()); assertTextPresent("Specimen Requests"); - click(Locator.linkWithText("By Individual Vial")); + clickAndWait(Locator.linkWithText("By Individual Vial")); assertElementPresent(Locator.navButton("Request Options")); assertTextPresent("Locked In Request", "Requestable", "Available", "Availability Reason", "Locked In Request Count", "Available Count", "Expected Available Count"); - click(Locator.linkWithText("Reports")); + clickAndWait(Locator.linkWithText("Reports")); assertTextPresent("Requested Vials by Type and Timepoint", "Request Summary"); clickButton("View"); assertTextPresent("Availability status"); @@ -158,10 +158,10 @@ private void verifyRequestsDisabled() { clickFolder(getFolderName()); assertTextNotPresent("Specimen Requests"); - click(Locator.linkWithText("By Individual Vial")); + clickAndWait(Locator.linkWithText("By Individual Vial")); assertElementNotPresent(Locator.navButton("Request Options")); assertTextNotPresent("Locked In Request", "Requestable", "Available", "Availability Reason", "Locked In Request Count", "Available Count", "Expected Available Count"); - click(Locator.linkWithText("Reports")); + clickAndWait(Locator.linkWithText("Reports")); assertTextNotPresent("Requested Vials by Type and Timepoint", "Request Summary"); clickButton("View"); assertTextNotPresent("Availability status"); From 17193feb871665695ef0793c87b7ff0199a58b2c Mon Sep 17 00:00:00 2001 From: Dave Bradlee Date: Thu, 16 May 2013 20:54:19 +0000 Subject: [PATCH 100/494] Multiple requests per aliquot feature. SVN r26452 |2013-05-16 20:54:19 +0000 --- src/org/labkey/test/tests/SpecimenTest.java | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/org/labkey/test/tests/SpecimenTest.java b/src/org/labkey/test/tests/SpecimenTest.java index 79e56c47..7d83669e 100644 --- a/src/org/labkey/test/tests/SpecimenTest.java +++ b/src/org/labkey/test/tests/SpecimenTest.java @@ -190,14 +190,9 @@ private void setupRequestabilityRules() // Verify that LOCKED_IN_REQUEST is the last rule waitForElement(Locator.xpath("//div[contains(@class, 'x-grid3-row-last')]//div[text()='Locked In Request Check']")); click(Locator.xpath("//div[contains(@class, 'x-grid3-row-last')]//div[text()='Locked In Request Check']")); - // Verify that LOCKED_IN_REQUEST rule cannot be moved or deleted - assertElementPresent(Locator.xpath("//table[@id='btn_deleteEngine' and contains(@class, 'x-item-disabled')]")); - assertElementPresent(Locator.xpath("//table[@id='btn_moveUp' and contains(@class, 'x-item-disabled')]")); - assertElementPresent(Locator.xpath("//table[@id='btn_moveDown' and contains(@class, 'x-item-disabled')]")); + click(Locator.xpath("//div[contains(@class, 'x-grid3-col-numberer') and text()='2']")); - assertElementPresent(Locator.xpath("//table[@id='btn_deleteEngine' and not(contains(@class, 'x-item-disabled'))]")); - assertElementPresent(Locator.xpath("//table[@id='btn_moveUp' and not(contains(@class, 'x-item-disabled'))]")); - assertElementPresent(Locator.xpath("//table[@id='btn_moveDown' and contains(@class, 'x-item-disabled')]")); + clickButton("Add Rule", 0); click(Locator.menuItem("Custom Query")); _extHelper.selectComboBoxItem(Locator.xpath("//div[@id='x-form-el-userQuery_schema']"), "study" ); @@ -320,10 +315,10 @@ private void uploadSpecimensFromFile() waitForElement(Locator.xpath("//textarea[@id='tsv3']")); setFormElement(Locator.xpath("//textarea[@id='tsv3']"), "AAA07XK5-01"); // try to add again clickButton("Submit", 0); // Submit button - waitForText("Specimen AAA07XK5-01 not available", 20000); + waitForText("Specimen AAA07XK5-01 is unavailable", 20000); setFormElement(Locator.xpath("//textarea[@id='tsv3']"), "AAA07XK5-02"); // try to add one that doesn't exist clickButton("Submit", 0); // Submit button - waitForText("Specimen AAA07XK5-02 not available", 20000); + waitForText("Specimen AAA07XK5-02 is unavailable", 20000); setFormElement(Locator.xpath("//textarea[@id='tsv3']"), "AAA07XK5-04\nAAA07XK5-06\nAAA07XSF-03"); // add different one clickButton("Submit"); // Submit button } From cd116a2113dfe4bf8a2f040cd1b2027cbc477111 Mon Sep 17 00:00:00 2001 From: Dave Bradlee Date: Fri, 17 May 2013 23:15:05 +0000 Subject: [PATCH 101/494] Add AliquotTest; SpecimenBaseTest has code shared between SpecimenTest and AliquotTest. SVN r26479 |2013-05-17 23:15:05 +0000 --- src/org/labkey/test/tests/AliquotTest.java | 199 ++++++++++++++++++ .../labkey/test/tests/SpecimenBaseTest.java | 118 +++++++++++ src/org/labkey/test/tests/SpecimenTest.java | 84 +------- 3 files changed, 318 insertions(+), 83 deletions(-) create mode 100644 src/org/labkey/test/tests/AliquotTest.java create mode 100644 src/org/labkey/test/tests/SpecimenBaseTest.java diff --git a/src/org/labkey/test/tests/AliquotTest.java b/src/org/labkey/test/tests/AliquotTest.java new file mode 100644 index 00000000..5a18f74e --- /dev/null +++ b/src/org/labkey/test/tests/AliquotTest.java @@ -0,0 +1,199 @@ +/* + * Copyright (c) 2007-2013 LabKey Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.labkey.test.tests; + +import org.labkey.test.Locator; +import org.labkey.test.util.LogMethod; + +/** + * Created with IntelliJ IDEA. + * User: davebradlee + * Date: 5/14/13 + * Time: 1:44 PM + * To change this template use File | Settings | File Templates. + */ +public class AliquotTest extends SpecimenBaseTest +{ + protected static final String PROJECT_NAME = "AliquotVerifyProject"; + + @Override + protected String getProjectName() + { + return PROJECT_NAME; + } + + @Override + protected boolean isFileUploadTest() + { + return true; + } + + @Override + @LogMethod(category = LogMethod.MethodType.SETUP) + protected void doCreateSteps() + { + enableEmailRecorder(); + initializeFolder(); + + clickButton("Create Study"); + setFormElement(Locator.name("label"), getStudyLabel()); + click(Locator.radioButtonByNameAndValue("simpleRepository", "false")); + clickButton("Create Study"); + + setPipelineRoot(getPipelinePath()); + + setupRequestabilityRules(); + startSpecimenImport(1); + waitForSpecimenImport(); + _studyHelper.createCustomParticipantGroup(getProjectName(), getFolderName(), "Category1", "Participant", null, false, PTIDS[0], PTIDS[1]); + setupSpecimenManagement(); + setupActorsAndGroups(); + setupDefaultRequirements(); + setupRequestForm(); + setupActorNotification(); + + } + + @Override + @LogMethod(category = LogMethod.MethodType.VERIFICATION) + protected void doVerifySteps() + { + createRequests(); + } + + @LogMethod + protected void setupRequestabilityRules() + { + // Create custom query to test requestability rules. + goToSchemaBrowser(); + selectQuery("study", SPECIMEN_DETAIL); + clickButton("Create New Query"); + setFormElement(Locator.name("ff_newQueryName"), REQUESTABILITY_QUERY); + clickAndWait(Locator.linkWithText("Create and Edit Source")); + setQueryEditorValue("queryText", + "SELECT \n" + + SPECIMEN_DETAIL + ".GlobalUniqueId AS GlobalUniqueId\n" + + "FROM " + SPECIMEN_DETAIL + "\n" + + "WHERE " + SPECIMEN_DETAIL + ".GlobalUniqueId='" + UNREQUESTABLE_SAMPLE + "'"); + clickButton("Save", 0); + waitForText("Saved", WAIT_FOR_JAVASCRIPT); + + clickFolder(getFolderName()); + waitAndClick(Locator.linkWithText("Manage Study")); + waitAndClick(Locator.linkWithText("Manage Requestability Rules")); + waitForElement(Locator.xpath("//div[contains(@class, 'x-grid3-row')]//div[text()='Locked In Request Check']")); + + clickButton("Add Rule", 0); + click(Locator.menuItem("Custom Query")); + _extHelper.selectComboBoxItem(Locator.xpath("//div[@id='x-form-el-userQuery_schema']"), "study" ); + _extHelper.selectComboBoxItem(Locator.xpath("//div[@id='x-form-el-userQuery_query']"), REQUESTABILITY_QUERY ); + _extHelper.selectComboBoxItem(Locator.xpath("//div[@id='x-form-el-userQuery_action']"), "Unavailable" ); + clickButton("Submit", 0); + + clickButton("Add Rule", 0); + click(Locator.menuItem("Locked While Processing Check")); + + // Remove Locked In Request + waitForElement(Locator.xpath("//div[contains(@class, 'x-grid3-row')]//div[text()='Locked In Request Check']")); + click(Locator.xpath("//div[contains(@class, 'x-grid3-row')]//div[text()='Locked In Request Check']")); + clickButton("Remove Rule", 0); + + clickButton("Save"); + } + + public static final String ALIQUOT_ONE = "AAA07XK5-03"; + public static final String ALIQUOT_TWO = "EBG002K4-25"; + public static final String ALIQUOT_ONE_CHECKBOX = "//input[@id='check_" + ALIQUOT_ONE + "']"; + public static final String ALIQUOT_ONE_CHECKBOX_DISABLED = "//input[@id='check_" + ALIQUOT_ONE + "' and @disabled]"; + public static final String UNAVAILABLE_ALIQUOT = "AAQ00032-02"; + public static final String ALIQUOT_TWO_CHECKBOX = "//input[@id='check_" + ALIQUOT_TWO + "']"; + public static final String UNAVAILABLE_ALIQUOT_DISABLED = "//input[@id='check_" + UNAVAILABLE_ALIQUOT + "' and @disabled]"; + + @LogMethod + private void createRequests() + { + clickFolder(getStudyLabel()); + clickAndWait(Locator.linkWithText("Specimen Data")); + clickAndWait(Locator.linkWithText("Blood (Whole)")); + + assertElementPresent(Locator.xpath(UNAVAILABLE_ALIQUOT_DISABLED)); + assertElementPresent(Locator.xpath(ALIQUOT_ONE_CHECKBOX)); + checkCheckbox(Locator.xpath(ALIQUOT_ONE_CHECKBOX)); + + _extHelper.clickMenuButton("Request Options", "Create New Request"); + selectOptionByText(Locator.name("destinationLocation"), DESTINATION_SITE); + setFormElement(Locator.id("input0"), "Assay Plan"); + setFormElement(Locator.id("input2"), "Comments"); + setFormElement(Locator.id("input1"), "Shipping"); + setFormElement(Locator.id("input3"), "sample last one input"); + clickButton("Create and View Details"); + assertTextPresent(DESTINATION_SITE); + assertTextPresent(ALIQUOT_ONE); + assertTextNotPresent("Complete"); + + // Check that aliquot we added is not available + clickFolder(getStudyLabel()); + clickAndWait(Locator.linkWithText("Specimen Data")); + clickAndWait(Locator.linkWithText("Blood (Whole)")); + assertElementPresent(Locator.xpath(ALIQUOT_ONE_CHECKBOX_DISABLED)); + assertElementPresent(Locator.xpath(ALIQUOT_ONE_CHECKBOX + "/../../td[contains(text(), 'This vial is unavailable because it is being processed')]")); + + // Now submit that request + _extHelper.clickMenuButton("Request Options", "View Existing Requests"); + clickButton("Submit", 0); + assertAlert("Once a request is submitted, its specimen list may no longer be modified. Continue?"); + assertTextPresent("Your request has been successfully submitted"); + + clickAndWait(Locator.linkWithText("Update Request")); + selectOptionByText(Locator.name("status"), "Completed"); + clickButton("Save Changes and Send Notifications"); + + // Now verify that that aliquot is available again + clickFolder(getStudyLabel()); + clickAndWait(Locator.linkWithText("Specimen Data")); + clickAndWait(Locator.linkWithText("Blood (Whole)")); + + assertElementPresent(Locator.xpath(ALIQUOT_ONE_CHECKBOX)); + checkCheckbox(Locator.xpath(ALIQUOT_ONE_CHECKBOX)); + + _extHelper.clickMenuButton("Request Options", "Create New Request"); + selectOptionByText(Locator.name("destinationLocation"), DESTINATION_SITE); + setFormElement(Locator.id("input0"), "Assay Plan"); + setFormElement(Locator.id("input2"), "Comments"); + setFormElement(Locator.id("input1"), "Shipping"); + setFormElement(Locator.id("input3"), "sample last one input"); + clickButton("Create and View Details"); + assertTextPresent(DESTINATION_SITE); + assertTextPresent(ALIQUOT_ONE); + assertTextNotPresent("Complete"); + + clickAndWait(Locator.linkWithText("Upload Specimen Ids")); + setFormElement(Locator.xpath("//textarea[@id='tsv3']"), ALIQUOT_TWO); // add specimen + clickButton("Submit"); // Submit button + assertTextPresent(ALIQUOT_ONE); + assertTextPresent(ALIQUOT_TWO); + checkCheckbox(Locator.checkboxByTitle("Select/unselect row")); // all individual item checkboxes have same name/title; should be first one + clickButton("Remove Selected"); + assertTextNotPresent(ALIQUOT_ONE); + assertTextPresent(ALIQUOT_TWO); + clickAndWait(Locator.linkWithText("Upload Specimen Ids")); + setFormElement(Locator.xpath("//textarea[@id='tsv3']"), ALIQUOT_ONE); // add specimen + clickButton("Submit"); // Submit button + assertTextPresent(ALIQUOT_ONE); + assertTextPresent(ALIQUOT_TWO); + } +} diff --git a/src/org/labkey/test/tests/SpecimenBaseTest.java b/src/org/labkey/test/tests/SpecimenBaseTest.java new file mode 100644 index 00000000..ccd97927 --- /dev/null +++ b/src/org/labkey/test/tests/SpecimenBaseTest.java @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2007-2013 LabKey Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.labkey.test.tests; + +import org.labkey.test.Locator; +import org.labkey.test.TestTimeoutException; +import org.labkey.test.util.LogMethod; + +/** + * Created with IntelliJ IDEA. + * User: davebradlee + * Date: 5/16/13 + * Time: 12:41 PM + * To change this template use File | Settings | File Templates. + */ + +// Base class for SpecimenTest and AliquotTest +public abstract class SpecimenBaseTest extends StudyBaseTestWD +{ + public static final String SPECIMEN_DETAIL = "SpecimenDetail"; + protected static final String DESTINATION_SITE = "Aurum Health KOSH Lab, Orkney, South Africa (Endpoint Lab, Repository)"; + protected static final String SOURCE_SITE = "Contract Lab Services, Johannesburg, South Africa (Repository, Clinic)"; + protected static final String USER1 = "user1@specimen.test"; + protected static final String USER2 = "user2@specimen.test"; + protected static final String REQUESTABILITY_QUERY = "RequestabilityRule"; + protected static final String UNREQUESTABLE_SAMPLE = "BAA07XNP-02"; + protected static final String[] PTIDS = {"999320396","999320812"}; + protected int _requestId; + + @Override + protected void doCleanup(boolean afterTest) throws TestTimeoutException + { + deleteUsers(afterTest, USER1, USER2); + super.doCleanup(afterTest); + } + + + @LogMethod + protected void setupActorsAndGroups() + { + clickAndWait(Locator.linkWithText("Manage Actors and Groups")); + setFormElement(Locator.name("newLabel"), "SLG"); + selectOptionByText(Locator.name("newPerSite"), "One Per Study"); + clickButton("Save"); + clickAndWait(Locator.linkWithText("Update Members")); + setFormElement(Locator.name("names"), USER1); + uncheckCheckbox(Locator.checkboxByName("sendEmail")); + clickButton("Update Members"); + setFormElement(Locator.name("newLabel"), "IRB"); + selectOptionByText(Locator.name("newPerSite"), "Multiple Per Study (Location Affiliated)"); + clickButton("Save"); + clickAndWait(Locator.linkWithText("Update Members").index(1)); + clickAndWait(Locator.linkWithText(DESTINATION_SITE)); + setFormElement(Locator.name("names"), USER2); + uncheckCheckbox(Locator.checkboxByName("sendEmail")); + clickAndWait(Locator.linkWithText("Update Members")); + clickFolder(getStudyLabel()); + } + + @LogMethod (quiet = true) + protected void setupDefaultRequirements() + { + clickAndWait(Locator.linkWithText("Manage Study")); + clickAndWait(Locator.linkWithText("Manage Default Requirements")); + selectOptionByText(Locator.name("originatorActor"), "IRB"); + setFormElement(Locator.name("originatorDescription"), "Originating IRB Approval"); + clickButton("Add Requirement"); + selectOptionByText(Locator.name("providerActor"), "IRB"); + setFormElement(Locator.name("providerDescription"), "Providing IRB Approval"); + clickAndWait(Locator.xpath("//input[@name='providerDescription']/../.." + Locator.navButton("Add Requirement").getPath())); + selectOptionByText(Locator.name("receiverActor"), "IRB"); + setFormElement(Locator.name("receiverDescription"), "Receiving IRB Approval"); + clickAndWait(Locator.xpath("//input[@name='receiverDescription']/../.." + Locator.navButton("Add Requirement").getPath())); + selectOptionByText(Locator.name("generalActor"), "SLG"); + setFormElement(Locator.name("generalDescription"), "SLG Approval"); + clickAndWait(Locator.xpath("//input[@name='generalDescription']/../.." + Locator.navButton("Add Requirement").getPath())); + clickTab("Manage"); + } + + @LogMethod (quiet = true) + protected void setupRequestForm() + { + clickAndWait(Locator.linkWithText("Manage New Request Form")); + clickButton("Add New Input", 0); + setFormElement(Locator.xpath("//descendant::input[@name='title'][4]"), "Last One"); + setFormElement(Locator.xpath("//descendant::input[@name='helpText'][4]"), "A test input"); + click(Locator.xpath("//descendant::input[@name='required'][4]")); + clickButton("Save"); + clickFolder(getStudyLabel()); + } + + @LogMethod + protected void setupActorNotification() + { + log("Check Configure Defaults for Actor Notification"); + clickFolder(getStudyLabel()); + clickAndWait(Locator.linkWithText("Manage")); + clickAndWait(Locator.linkWithText("Manage Notifications")); + assertTextPresent("Default Email Recipients"); + checkRadioButton(Locator.radioButtonByNameAndValue("defaultEmailNotify", "All")); + clickButton("Save"); + } + +} diff --git a/src/org/labkey/test/tests/SpecimenTest.java b/src/org/labkey/test/tests/SpecimenTest.java index 7d83669e..1ba1172c 100644 --- a/src/org/labkey/test/tests/SpecimenTest.java +++ b/src/org/labkey/test/tests/SpecimenTest.java @@ -41,19 +41,10 @@ * Date: Mar 9, 2006 * Time: 1:54:57 PM */ -public class SpecimenTest extends StudyBaseTestWD +public class SpecimenTest extends SpecimenBaseTest { protected static final String PROJECT_NAME = "SpecimenVerifyProject"; - public static final String SPECIMEN_DETAIL = "SpecimenDetail"; - private static final String DESTINATION_SITE = "Aurum Health KOSH Lab, Orkney, South Africa (Endpoint Lab, Repository)"; - private static final String SOURCE_SITE = "Contract Lab Services, Johannesburg, South Africa (Repository, Clinic)"; - private static final String USER1 = "user1@specimen.test"; - private static final String USER2 = "user2@specimen.test"; - private static final String REQUESTABILITY_QUERY = "RequestabilityRule"; - private static final String UNREQUESTABLE_SAMPLE = "BAA07XNP-02"; private final File REQUEST_ATTACHMENT = new File(getPipelinePath() + "specimens", "labs.txt"); - private static final String[] PTIDS = {"999320396","999320812"}; - private int _requestId; public String getAssociatedModuleDirectory() @@ -73,13 +64,6 @@ protected boolean isFileUploadTest() return true; } - @Override - protected void doCleanup(boolean afterTest) throws TestTimeoutException - { - deleteUsers(afterTest, USER1, USER2); - super.doCleanup(afterTest); - } - @Override @LogMethod(category = LogMethod.MethodType.SETUP) protected void doCreateSteps() @@ -227,72 +211,6 @@ private void checkTubeType() assertTextPresent("15ml Cryovial", 3); } - @LogMethod - private void setupActorsAndGroups() - { - clickAndWait(Locator.linkWithText("Manage Actors and Groups")); - setFormElement(Locator.name("newLabel"), "SLG"); - selectOptionByText(Locator.name("newPerSite"), "One Per Study"); - clickButton("Save"); - clickAndWait(Locator.linkWithText("Update Members")); - setFormElement(Locator.name("names"), USER1); - uncheckCheckbox("sendEmail"); - clickButton("Update Members"); - setFormElement(Locator.name("newLabel"), "IRB"); - selectOptionByText(Locator.name("newPerSite"), "Multiple Per Study (Location Affiliated)"); - clickButton("Save"); - clickAndWait(Locator.linkWithText("Update Members").index(1)); - clickAndWait(Locator.linkWithText(DESTINATION_SITE)); - setFormElement(Locator.name("names"), USER2); - uncheckCheckbox("sendEmail"); - clickAndWait(Locator.linkWithText("Update Members")); - clickFolder(getStudyLabel()); - } - - @LogMethod (quiet = true) - private void setupDefaultRequirements() - { - clickAndWait(Locator.linkWithText("Manage Study")); - clickAndWait(Locator.linkWithText("Manage Default Requirements")); - selectOptionByText(Locator.name("originatorActor"), "IRB"); - setFormElement(Locator.name("originatorDescription"), "Originating IRB Approval"); - clickButton("Add Requirement"); - selectOptionByText(Locator.name("providerActor"), "IRB"); - setFormElement(Locator.name("providerDescription"), "Providing IRB Approval"); - clickAndWait(Locator.xpath("//input[@name='providerDescription']/../.." + Locator.navButton("Add Requirement").getPath())); - selectOptionByText(Locator.name("receiverActor"), "IRB"); - setFormElement(Locator.name("receiverDescription"), "Receiving IRB Approval"); - clickAndWait(Locator.xpath("//input[@name='receiverDescription']/../.." + Locator.navButton("Add Requirement").getPath())); - selectOptionByText(Locator.name("generalActor"), "SLG"); - setFormElement(Locator.name("generalDescription"), "SLG Approval"); - clickAndWait(Locator.xpath("//input[@name='generalDescription']/../.." + Locator.navButton("Add Requirement").getPath())); - clickTab("Manage"); - } - - @LogMethod (quiet = true) - private void setupRequestForm() - { - clickAndWait(Locator.linkWithText("Manage New Request Form")); - clickButton("Add New Input", 0); - setFormElement(Locator.xpath("//descendant::input[@name='title'][4]"), "Last One"); - setFormElement(Locator.xpath("//descendant::input[@name='helpText'][4]"), "A test input"); - click(Locator.xpath("//descendant::input[@name='required'][4]")); - clickButton("Save"); - clickFolder(getStudyLabel()); - } - - @LogMethod - private void setupActorNotification() - { - log("Check Configure Defaults for Actor Notification"); - clickFolder(getStudyLabel()); - clickAndWait(Locator.linkWithText("Manage")); - clickAndWait(Locator.linkWithText("Manage Notifications")); - assertTextPresent("Default Email Recipients"); - checkRadioButton("defaultEmailNotify", "All"); - clickButton("Save"); - } - @LogMethod private void uploadSpecimensFromFile() { From 6970f23d8e97d7bf1418683b1841604575f1e316 Mon Sep 17 00:00:00 2001 From: Trey Chadick Date: Wed, 22 May 2013 20:31:36 +0000 Subject: [PATCH 102/494] Remove IntelliJ template comments SVN r26555 |2013-05-22 20:31:36 +0000 --- src/org/labkey/test/tests/AliquotTest.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/org/labkey/test/tests/AliquotTest.java b/src/org/labkey/test/tests/AliquotTest.java index 5a18f74e..8eba16b3 100644 --- a/src/org/labkey/test/tests/AliquotTest.java +++ b/src/org/labkey/test/tests/AliquotTest.java @@ -20,11 +20,9 @@ import org.labkey.test.util.LogMethod; /** - * Created with IntelliJ IDEA. * User: davebradlee * Date: 5/14/13 * Time: 1:44 PM - * To change this template use File | Settings | File Templates. */ public class AliquotTest extends SpecimenBaseTest { From 8116d8e3170f89802b3fa8cd7f3523f1055bfbb7 Mon Sep 17 00:00:00 2001 From: Trey Chadick Date: Tue, 28 May 2013 16:47:05 +0000 Subject: [PATCH 103/494] Wait for specimen webpart to render SVN r26619 |2013-05-28 16:47:05 +0000 --- src/org/labkey/test/tests/AliquotTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/org/labkey/test/tests/AliquotTest.java b/src/org/labkey/test/tests/AliquotTest.java index 8eba16b3..52b8a31b 100644 --- a/src/org/labkey/test/tests/AliquotTest.java +++ b/src/org/labkey/test/tests/AliquotTest.java @@ -126,7 +126,7 @@ private void createRequests() { clickFolder(getStudyLabel()); clickAndWait(Locator.linkWithText("Specimen Data")); - clickAndWait(Locator.linkWithText("Blood (Whole)")); + waitAndClickAndWait(Locator.linkWithText("Blood (Whole)")); assertElementPresent(Locator.xpath(UNAVAILABLE_ALIQUOT_DISABLED)); assertElementPresent(Locator.xpath(ALIQUOT_ONE_CHECKBOX)); From 258519b58fb12b644d25486477def8f89d9d18de Mon Sep 17 00:00:00 2001 From: Trey Chadick Date: Wed, 29 May 2013 15:56:41 +0000 Subject: [PATCH 104/494] Wait for specimen webpart SVN r26644 |2013-05-29 15:56:41 +0000 --- src/org/labkey/test/tests/AliquotTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/org/labkey/test/tests/AliquotTest.java b/src/org/labkey/test/tests/AliquotTest.java index 52b8a31b..eadf2e46 100644 --- a/src/org/labkey/test/tests/AliquotTest.java +++ b/src/org/labkey/test/tests/AliquotTest.java @@ -146,7 +146,7 @@ private void createRequests() // Check that aliquot we added is not available clickFolder(getStudyLabel()); clickAndWait(Locator.linkWithText("Specimen Data")); - clickAndWait(Locator.linkWithText("Blood (Whole)")); + waitAndClickAndWait(Locator.linkWithText("Blood (Whole)")); assertElementPresent(Locator.xpath(ALIQUOT_ONE_CHECKBOX_DISABLED)); assertElementPresent(Locator.xpath(ALIQUOT_ONE_CHECKBOX + "/../../td[contains(text(), 'This vial is unavailable because it is being processed')]")); @@ -163,7 +163,7 @@ private void createRequests() // Now verify that that aliquot is available again clickFolder(getStudyLabel()); clickAndWait(Locator.linkWithText("Specimen Data")); - clickAndWait(Locator.linkWithText("Blood (Whole)")); + waitAndClickAndWait(Locator.linkWithText("Blood (Whole)")); assertElementPresent(Locator.xpath(ALIQUOT_ONE_CHECKBOX)); checkCheckbox(Locator.xpath(ALIQUOT_ONE_CHECKBOX)); From 98ad484f2b0fdb499508d924a6fc2577c1b786f2 Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Wed, 29 May 2013 21:29:15 +0000 Subject: [PATCH 105/494] Update licenses SVN r26658 |2013-05-29 21:29:15 +0000 --- src/org/labkey/test/tests/AliquotTest.java | 2 +- src/org/labkey/test/tests/SpecimenBaseTest.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/org/labkey/test/tests/AliquotTest.java b/src/org/labkey/test/tests/AliquotTest.java index eadf2e46..bc4a9511 100644 --- a/src/org/labkey/test/tests/AliquotTest.java +++ b/src/org/labkey/test/tests/AliquotTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007-2013 LabKey Corporation + * Copyright (c) 2013 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/org/labkey/test/tests/SpecimenBaseTest.java b/src/org/labkey/test/tests/SpecimenBaseTest.java index ccd97927..c9fdceb2 100644 --- a/src/org/labkey/test/tests/SpecimenBaseTest.java +++ b/src/org/labkey/test/tests/SpecimenBaseTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007-2013 LabKey Corporation + * Copyright (c) 2013 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. From c5b652cb1aa407d62ecaa93aa2176e15565ff2b9 Mon Sep 17 00:00:00 2001 From: Trey Chadick Date: Mon, 3 Jun 2013 16:46:17 +0000 Subject: [PATCH 106/494] Wait for success text SVN r26738 |2013-06-03 16:46:17 +0000 --- src/org/labkey/test/tests/AliquotTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/org/labkey/test/tests/AliquotTest.java b/src/org/labkey/test/tests/AliquotTest.java index bc4a9511..432096f4 100644 --- a/src/org/labkey/test/tests/AliquotTest.java +++ b/src/org/labkey/test/tests/AliquotTest.java @@ -154,7 +154,7 @@ private void createRequests() _extHelper.clickMenuButton("Request Options", "View Existing Requests"); clickButton("Submit", 0); assertAlert("Once a request is submitted, its specimen list may no longer be modified. Continue?"); - assertTextPresent("Your request has been successfully submitted"); + waitForElement(Locator.css("h3").withText("Your request has been successfully submitted.")); clickAndWait(Locator.linkWithText("Update Request")); selectOptionByText(Locator.name("status"), "Completed"); From 189ae2b8712d59363df686ad43b91d3bb22fc10e Mon Sep 17 00:00:00 2001 From: Karl Lum Date: Mon, 10 Jun 2013 00:41:08 +0000 Subject: [PATCH 107/494] convert script editor to use codemirror delete the editarea library now that it is no longer used rename test helper setQueryEditorValue -> setCodeEditorValue remove some obsolete test helper fn's fix for reportTest SVN r26858 |2013-06-10 00:41:08 +0000 --- src/org/labkey/test/tests/AliquotTest.java | 2 +- src/org/labkey/test/tests/SpecimenTest.java | 9 ++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/org/labkey/test/tests/AliquotTest.java b/src/org/labkey/test/tests/AliquotTest.java index 432096f4..86585d7a 100644 --- a/src/org/labkey/test/tests/AliquotTest.java +++ b/src/org/labkey/test/tests/AliquotTest.java @@ -82,7 +82,7 @@ protected void setupRequestabilityRules() clickButton("Create New Query"); setFormElement(Locator.name("ff_newQueryName"), REQUESTABILITY_QUERY); clickAndWait(Locator.linkWithText("Create and Edit Source")); - setQueryEditorValue("queryText", + setCodeEditorValue("queryText", "SELECT \n" + SPECIMEN_DETAIL + ".GlobalUniqueId AS GlobalUniqueId\n" + "FROM " + SPECIMEN_DETAIL + "\n" + diff --git a/src/org/labkey/test/tests/SpecimenTest.java b/src/org/labkey/test/tests/SpecimenTest.java index 1ba1172c..1843fe34 100644 --- a/src/org/labkey/test/tests/SpecimenTest.java +++ b/src/org/labkey/test/tests/SpecimenTest.java @@ -20,7 +20,6 @@ import org.apache.http.HttpStatus; import org.junit.Assert; import org.labkey.test.Locator; -import org.labkey.test.TestTimeoutException; import org.labkey.test.WebTestHelper; import org.labkey.test.util.DataRegionTable; import org.labkey.test.util.LabKeyExpectedConditions; @@ -160,11 +159,11 @@ private void setupRequestabilityRules() clickButton("Create New Query"); setFormElement(Locator.name("ff_newQueryName"), REQUESTABILITY_QUERY); clickAndWait(Locator.linkWithText("Create and Edit Source")); - setQueryEditorValue("queryText", + setCodeEditorValue("queryText", "SELECT \n" + - SPECIMEN_DETAIL + ".GlobalUniqueId AS GlobalUniqueId\n" + - "FROM " + SPECIMEN_DETAIL + "\n" + - "WHERE " + SPECIMEN_DETAIL + ".GlobalUniqueId='" + UNREQUESTABLE_SAMPLE + "'"); + SPECIMEN_DETAIL + ".GlobalUniqueId AS GlobalUniqueId\n" + + "FROM " + SPECIMEN_DETAIL + "\n" + + "WHERE " + SPECIMEN_DETAIL + ".GlobalUniqueId='" + UNREQUESTABLE_SAMPLE + "'"); clickButton("Save", 0); waitForText("Saved", WAIT_FOR_JAVASCRIPT); From 643b9712d21d3424e9f89874d503055903b55e26 Mon Sep 17 00:00:00 2001 From: Trey Chadick Date: Sat, 15 Jun 2013 00:54:59 +0000 Subject: [PATCH 108/494] Clean up various inspection warnings SVN r27009 |2013-06-15 00:54:59 +0000 --- src/org/labkey/test/tests/SpecimenBaseTest.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/org/labkey/test/tests/SpecimenBaseTest.java b/src/org/labkey/test/tests/SpecimenBaseTest.java index c9fdceb2..9b333977 100644 --- a/src/org/labkey/test/tests/SpecimenBaseTest.java +++ b/src/org/labkey/test/tests/SpecimenBaseTest.java @@ -21,11 +21,8 @@ import org.labkey.test.util.LogMethod; /** - * Created with IntelliJ IDEA. * User: davebradlee * Date: 5/16/13 - * Time: 12:41 PM - * To change this template use File | Settings | File Templates. */ // Base class for SpecimenTest and AliquotTest From 97d19e5735d90bb92ee5f4841db42c9da6aff320 Mon Sep 17 00:00:00 2001 From: Trey Chadick Date: Tue, 18 Jun 2013 18:13:19 +0000 Subject: [PATCH 109/494] Test cleanup Remove and refactor usages for many deprecated methods Remove some unused/unneeded/redundant/obsolete methods Remove obsolete IE workarounds Use java.nio for reading and copying files Use try-with-resources SVN r27043 |2013-06-18 18:13:19 +0000 --- src/org/labkey/test/tests/SpecimenTest.java | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/org/labkey/test/tests/SpecimenTest.java b/src/org/labkey/test/tests/SpecimenTest.java index 1843fe34..e4c47637 100644 --- a/src/org/labkey/test/tests/SpecimenTest.java +++ b/src/org/labkey/test/tests/SpecimenTest.java @@ -245,14 +245,15 @@ private void verifyActorDetails() { // Check each Actor's Details for "Default Actor Notification" feature; // In Details, for each actor the ether Notify checkbox should be set or disabled, because we set Notifications to All - List detailsLinks = findAllMatches(Locator.xpath("//td[a='Details']/a")); - for (Locator link : detailsLinks) + Locator detailsLink = Locator.xpath("//td[a='Details']/a"); + int linkCount = getElementCount(detailsLink); + for (int i = 0; i < linkCount; i++) { - clickAndWait(link); - List allCheckBoxes = findAllMatches(Locator.xpath("//input[@type='checkbox' and @name='notificationIdPairs']")); - List checkedCheckBoxes = findAllMatches(Locator.xpath("//input[@type='checkbox' and @name='notificationIdPairs' and @checked]")); - List disabledCheckBoxes = findAllMatches(Locator.xpath("//input[@type='checkbox' and @name='notificationIdPairs' and @disabled]")); - Assert.assertTrue("Actor Notification: All actors should be notified if addresses configured.", allCheckBoxes.size() == checkedCheckBoxes.size() + disabledCheckBoxes.size()); + clickAndWait(detailsLink.index(i)); + int allCheckBoxes = getElementCount(Locator.xpath("//input[@type='checkbox' and @name='notificationIdPairs']")); + int checkedCheckBoxes = getElementCount(Locator.xpath("//input[@type='checkbox' and @name='notificationIdPairs' and @checked]")); + int disabledCheckBoxes = getElementCount(Locator.xpath("//input[@type='checkbox' and @name='notificationIdPairs' and @disabled]")); + Assert.assertTrue("Actor Notification: All actors should be notified if addresses configured.", allCheckBoxes == checkedCheckBoxes + disabledCheckBoxes); clickButton("Cancel"); } From 4c290ae5115bc46357d0daaffc8d5199778bb843 Mon Sep 17 00:00:00 2001 From: Cory Nathe Date: Thu, 27 Jun 2013 15:00:36 +0000 Subject: [PATCH 110/494] Fix SpecimenSettingsImporter for importing location types (typo) Move around some specimen helper function in preparation for new SpecimenExportTest Convert SpecimenTest and AliquotTest to work with Chrome SVN r27243 |2013-06-27 15:00:36 +0000 --- src/org/labkey/test/tests/AliquotTest.java | 27 ++-- .../labkey/test/tests/SpecimenBaseTest.java | 48 ++++++- src/org/labkey/test/tests/SpecimenTest.java | 136 +++++++----------- 3 files changed, 113 insertions(+), 98 deletions(-) diff --git a/src/org/labkey/test/tests/AliquotTest.java b/src/org/labkey/test/tests/AliquotTest.java index 86585d7a..676f6fb2 100644 --- a/src/org/labkey/test/tests/AliquotTest.java +++ b/src/org/labkey/test/tests/AliquotTest.java @@ -40,6 +40,12 @@ protected boolean isFileUploadTest() return true; } + @Override + protected BrowserType bestBrowser() + { + return BrowserType.CHROME; + } + @Override @LogMethod(category = LogMethod.MethodType.SETUP) protected void doCreateSteps() @@ -58,7 +64,7 @@ protected void doCreateSteps() startSpecimenImport(1); waitForSpecimenImport(); _studyHelper.createCustomParticipantGroup(getProjectName(), getFolderName(), "Category1", "Participant", null, false, PTIDS[0], PTIDS[1]); - setupSpecimenManagement(); + setupRequestStatuses(); setupActorsAndGroups(); setupDefaultRequirements(); setupRequestForm(); @@ -73,6 +79,7 @@ protected void doVerifySteps() createRequests(); } + @Override @LogMethod protected void setupRequestabilityRules() { @@ -124,9 +131,9 @@ protected void setupRequestabilityRules() @LogMethod private void createRequests() { - clickFolder(getStudyLabel()); - clickAndWait(Locator.linkWithText("Specimen Data")); - waitAndClickAndWait(Locator.linkWithText("Blood (Whole)")); + clickTab("Specimen Data"); + waitForVialSearch(); + clickAndWait(Locator.linkWithText("Blood (Whole)")); assertElementPresent(Locator.xpath(UNAVAILABLE_ALIQUOT_DISABLED)); assertElementPresent(Locator.xpath(ALIQUOT_ONE_CHECKBOX)); @@ -144,9 +151,9 @@ private void createRequests() assertTextNotPresent("Complete"); // Check that aliquot we added is not available - clickFolder(getStudyLabel()); - clickAndWait(Locator.linkWithText("Specimen Data")); - waitAndClickAndWait(Locator.linkWithText("Blood (Whole)")); + clickTab("Specimen Data"); + waitForVialSearch(); + clickAndWait(Locator.linkWithText("Blood (Whole)")); assertElementPresent(Locator.xpath(ALIQUOT_ONE_CHECKBOX_DISABLED)); assertElementPresent(Locator.xpath(ALIQUOT_ONE_CHECKBOX + "/../../td[contains(text(), 'This vial is unavailable because it is being processed')]")); @@ -161,9 +168,9 @@ private void createRequests() clickButton("Save Changes and Send Notifications"); // Now verify that that aliquot is available again - clickFolder(getStudyLabel()); - clickAndWait(Locator.linkWithText("Specimen Data")); - waitAndClickAndWait(Locator.linkWithText("Blood (Whole)")); + clickTab("Specimen Data"); + waitForVialSearch(); + clickAndWait(Locator.linkWithText("Blood (Whole)")); assertElementPresent(Locator.xpath(ALIQUOT_ONE_CHECKBOX)); checkCheckbox(Locator.xpath(ALIQUOT_ONE_CHECKBOX)); diff --git a/src/org/labkey/test/tests/SpecimenBaseTest.java b/src/org/labkey/test/tests/SpecimenBaseTest.java index 9b333977..be284bc7 100644 --- a/src/org/labkey/test/tests/SpecimenBaseTest.java +++ b/src/org/labkey/test/tests/SpecimenBaseTest.java @@ -45,10 +45,45 @@ protected void doCleanup(boolean afterTest) throws TestTimeoutException super.doCleanup(afterTest); } + @LogMethod + protected void setupRequestabilityRules() + { + // Create custom query to test requestability rules. + goToSchemaBrowser(); + selectQuery("study", SPECIMEN_DETAIL); + clickButton("Create New Query"); + setFormElement(Locator.name("ff_newQueryName"), REQUESTABILITY_QUERY); + clickAndWait(Locator.linkWithText("Create and Edit Source")); + setCodeEditorValue("queryText", + "SELECT \n" + + SPECIMEN_DETAIL + ".GlobalUniqueId AS GlobalUniqueId\n" + + "FROM " + SPECIMEN_DETAIL + "\n" + + "WHERE " + SPECIMEN_DETAIL + ".GlobalUniqueId='" + UNREQUESTABLE_SAMPLE + "'"); + clickButton("Save", 0); + waitForText("Saved", WAIT_FOR_JAVASCRIPT); + + clickFolder(getFolderName()); + waitAndClick(Locator.linkWithText("Manage Study")); + waitAndClick(Locator.linkWithText("Manage Requestability Rules")); + // Verify that LOCKED_IN_REQUEST is the last rule + waitForElement(Locator.xpath("//div[contains(@class, 'x-grid3-row-last')]//div[text()='Locked In Request Check']")); + click(Locator.xpath("//div[contains(@class, 'x-grid3-row-last')]//div[text()='Locked In Request Check']")); + + click(Locator.xpath("//div[contains(@class, 'x-grid3-col-numberer') and text()='2']")); + + clickButton("Add Rule", 0); + click(Locator.menuItem("Custom Query")); + _extHelper.selectComboBoxItem(Locator.xpath("//div[@id='x-form-el-userQuery_schema']"), "study" ); + _extHelper.selectComboBoxItem(Locator.xpath("//div[@id='x-form-el-userQuery_query']"), REQUESTABILITY_QUERY ); + _extHelper.selectComboBoxItem(Locator.xpath("//div[@id='x-form-el-userQuery_action']"), "Unavailable" ); + clickButton("Submit",0); + clickButton("Save"); + } @LogMethod protected void setupActorsAndGroups() { + clickTab("Manage"); clickAndWait(Locator.linkWithText("Manage Actors and Groups")); setFormElement(Locator.name("newLabel"), "SLG"); selectOptionByText(Locator.name("newPerSite"), "One Per Study"); @@ -65,13 +100,12 @@ protected void setupActorsAndGroups() setFormElement(Locator.name("names"), USER2); uncheckCheckbox(Locator.checkboxByName("sendEmail")); clickAndWait(Locator.linkWithText("Update Members")); - clickFolder(getStudyLabel()); } @LogMethod (quiet = true) protected void setupDefaultRequirements() { - clickAndWait(Locator.linkWithText("Manage Study")); + clickTab("Manage"); clickAndWait(Locator.linkWithText("Manage Default Requirements")); selectOptionByText(Locator.name("originatorActor"), "IRB"); setFormElement(Locator.name("originatorDescription"), "Originating IRB Approval"); @@ -85,31 +119,33 @@ protected void setupDefaultRequirements() selectOptionByText(Locator.name("generalActor"), "SLG"); setFormElement(Locator.name("generalDescription"), "SLG Approval"); clickAndWait(Locator.xpath("//input[@name='generalDescription']/../.." + Locator.navButton("Add Requirement").getPath())); - clickTab("Manage"); } @LogMethod (quiet = true) protected void setupRequestForm() { + clickTab("Manage"); clickAndWait(Locator.linkWithText("Manage New Request Form")); clickButton("Add New Input", 0); setFormElement(Locator.xpath("//descendant::input[@name='title'][4]"), "Last One"); setFormElement(Locator.xpath("//descendant::input[@name='helpText'][4]"), "A test input"); click(Locator.xpath("//descendant::input[@name='required'][4]")); clickButton("Save"); - clickFolder(getStudyLabel()); } @LogMethod protected void setupActorNotification() { log("Check Configure Defaults for Actor Notification"); - clickFolder(getStudyLabel()); - clickAndWait(Locator.linkWithText("Manage")); + clickTab("Manage"); clickAndWait(Locator.linkWithText("Manage Notifications")); assertTextPresent("Default Email Recipients"); checkRadioButton(Locator.radioButtonByNameAndValue("defaultEmailNotify", "All")); clickButton("Save"); } + protected void waitForVialSearch() + { + waitForElement(Locator.css(".specimenSearchLoaded")); + } } diff --git a/src/org/labkey/test/tests/SpecimenTest.java b/src/org/labkey/test/tests/SpecimenTest.java index e4c47637..35e3d025 100644 --- a/src/org/labkey/test/tests/SpecimenTest.java +++ b/src/org/labkey/test/tests/SpecimenTest.java @@ -26,6 +26,7 @@ import org.labkey.test.util.LogMethod; import org.labkey.test.util.LoggedParam; import org.labkey.test.util.PasswordUtil; +import org.labkey.test.util.PortalHelper; import org.labkey.test.util.ext4cmp.Ext4FieldRefWD; import java.io.File; @@ -33,7 +34,6 @@ import java.text.SimpleDateFormat; import java.util.Arrays; import java.util.Date; -import java.util.List; /** * User: brittp @@ -44,7 +44,7 @@ public class SpecimenTest extends SpecimenBaseTest { protected static final String PROJECT_NAME = "SpecimenVerifyProject"; private final File REQUEST_ATTACHMENT = new File(getPipelinePath() + "specimens", "labs.txt"); - + private final PortalHelper _portalHelper = new PortalHelper(this); public String getAssociatedModuleDirectory() { @@ -63,6 +63,12 @@ protected boolean isFileUploadTest() return true; } + @Override + protected BrowserType bestBrowser() + { + return BrowserType.CHROME; + } + @Override @LogMethod(category = LogMethod.MethodType.SETUP) protected void doCreateSteps() @@ -82,7 +88,7 @@ protected void doCreateSteps() waitForSpecimenImport(); _studyHelper.createCustomParticipantGroup(getProjectName(), getFolderName(), "Category1", "Participant", null, false, PTIDS[0], PTIDS[1]); checkTubeType(); - setupSpecimenManagement(); + setupRequestStatuses(); setupActorsAndGroups(); setupDefaultRequirements(); setupRequestForm(); @@ -150,47 +156,12 @@ private void verifyRequestsDisabled() assertTextNotPresent("Availability status"); } - @LogMethod - private void setupRequestabilityRules() - { - // Create custom query to test requestability rules. - goToSchemaBrowser(); - selectQuery("study", SPECIMEN_DETAIL); - clickButton("Create New Query"); - setFormElement(Locator.name("ff_newQueryName"), REQUESTABILITY_QUERY); - clickAndWait(Locator.linkWithText("Create and Edit Source")); - setCodeEditorValue("queryText", - "SELECT \n" + - SPECIMEN_DETAIL + ".GlobalUniqueId AS GlobalUniqueId\n" + - "FROM " + SPECIMEN_DETAIL + "\n" + - "WHERE " + SPECIMEN_DETAIL + ".GlobalUniqueId='" + UNREQUESTABLE_SAMPLE + "'"); - clickButton("Save", 0); - waitForText("Saved", WAIT_FOR_JAVASCRIPT); - - clickFolder(getFolderName()); - waitAndClick(Locator.linkWithText("Manage Study")); - waitAndClick(Locator.linkWithText("Manage Requestability Rules")); - // Verify that LOCKED_IN_REQUEST is the last rule - waitForElement(Locator.xpath("//div[contains(@class, 'x-grid3-row-last')]//div[text()='Locked In Request Check']")); - click(Locator.xpath("//div[contains(@class, 'x-grid3-row-last')]//div[text()='Locked In Request Check']")); - - click(Locator.xpath("//div[contains(@class, 'x-grid3-col-numberer') and text()='2']")); - - clickButton("Add Rule", 0); - click(Locator.menuItem("Custom Query")); - _extHelper.selectComboBoxItem(Locator.xpath("//div[@id='x-form-el-userQuery_schema']"), "study" ); - _extHelper.selectComboBoxItem(Locator.xpath("//div[@id='x-form-el-userQuery_query']"), REQUESTABILITY_QUERY ); - _extHelper.selectComboBoxItem(Locator.xpath("//div[@id='x-form-el-userQuery_action']"), "Unavailable" ); - clickButton("Submit",0); - clickButton("Save"); - } - @LogMethod private void checkTubeType() { // Field check for Tube Type column (including conflict) - clickFolder(getStudyLabel()); - addWebPart("Specimens"); + clickTab("Overview"); + _portalHelper.addWebPart("Specimens"); waitAndClickAndWait(Locator.linkWithText("By Individual Vial")); setFilter(SPECIMEN_DETAIL, "PrimaryType", "Is Blank"); // Verify that there's only one vial of unknown type: @@ -203,8 +174,8 @@ private void checkTubeType() assertTextPresent("20ml Cryovial"); assertTextPresent("25ml Cryovial"); clickAndWait(Locator.linkWithText("Specimen Overview")); - click(Locator.linkWithText("Vials by Derivative Type")); - waitAndClick(WAIT_FOR_JAVASCRIPT, Locator.linkWithText("Tear Flo Strips"), WAIT_FOR_PAGE); + waitAndClick(Locator.xpath("//span[text()='Vials by Derivative Type']/../img")); + waitAndClickAndWait(Locator.linkWithText("Tear Flo Strips")); // For these three vials, there should be no conflict in TubeType, so we should see the text once for each of three vials: assertLinkPresentWithTextCount("[history]", 3); assertTextPresent("15ml Cryovial", 3); @@ -214,10 +185,10 @@ private void checkTubeType() private void uploadSpecimensFromFile() { log("Check Upload Specimen List from file"); - clickFolder(getStudyLabel()); - clickAndWait(Locator.linkWithText("Specimen Data")); - waitAndClick(Locator.linkWithText("Specimen Requests")); // expand node in Specimens webpart - clickAndWait(Locator.linkWithText("Create New Request")); + clickTab("Specimen Data"); + waitForVialSearch(); + click(Locator.imageWithAltText("New Request Icon", true)); + waitForElement(Locator.name("destinationLocation")); selectOptionByText(Locator.name("destinationLocation"), DESTINATION_SITE); setFormElement(Locator.id("input0"), "Assay Plan"); setFormElement(Locator.id("input2"), "Comments"); @@ -269,10 +240,11 @@ private void verifyActorDetails() @LogMethod private void createRequest() { - clickFolder(getStudyLabel()); - clickAndWait(Locator.linkWithText("Specimen Data")); - click(Locator.linkWithText("Vials by Derivative Type")); - waitAndClick(WAIT_FOR_JAVASCRIPT, Locator.linkWithText("Plasma, Unknown Processing"), WAIT_FOR_PAGE); + clickTab("Specimen Data"); + waitForVialSearch(); + click(Locator.xpath("//span[text()='Vials by Derivative Type']/../img")); + waitForElement(Locator.linkWithText("Plasma, Unknown Processing")); + clickAndWait(Locator.linkWithText("Plasma, Unknown Processing")); // Verify unavailable sample assertElementPresent(Locator.xpath("//input[@id='check_" + UNREQUESTABLE_SAMPLE + "' and @disabled]")); assertElementPresent(Locator.xpath("//input[@id='check_" + UNREQUESTABLE_SAMPLE + "']/../../td[contains(text(), 'This vial is unavailable because it was found in the set called \"" + REQUESTABILITY_QUERY + "\".')]")); @@ -308,12 +280,13 @@ private void createRequest() // verify that the swab specimen isn't present yet assertTextNotPresent("DAA07YGW-01"); assertTextNotPresent("Complete"); - clickFolder(getStudyLabel()); // add additional specimens - clickAndWait(Locator.linkWithText("Specimen Data")); - waitAndClick(Locator.linkWithText("Vials by Derivative Type")); - waitAndClick(WAIT_FOR_JAVASCRIPT, Locator.linkWithText("Swab"), WAIT_FOR_PAGE); + clickTab("Specimen Data"); + waitForVialSearch(); + click(Locator.xpath("//span[text()='Vials by Derivative Type']/../img")); + waitForElement(Locator.linkWithText("Swab")); + clickAndWait(Locator.linkWithText("Swab")); checkCheckbox(".toggle"); clickMenuButtonAndContinue("Request Options", "Add To Existing Request"); _extHelper.waitForExtDialog("Request Vial", WAIT_FOR_JAVASCRIPT); @@ -429,10 +402,11 @@ private void verifyAdditionalRequestFields() popLocation(); log("verifying column changes"); - clickFolder(getStudyLabel()); - clickAndWait(Locator.linkWithText("Specimen Data")); - waitAndClick(Locator.linkWithText("Specimen Requests")); // expand node in Specimens webpart - clickAndWait(Locator.xpath("//a[text() = 'View Current Requests']")); + clickTab("Specimen Data"); + waitForVialSearch(); + click(Locator.xpath("//span[text() = 'Specimen Requests']/../img")); // expand node in Specimens webpart + waitForElement(Locator.linkWithText("View Current Requests")); + clickAndWait(Locator.linkWithText("View Current Requests")); clickButton("Details"); clickAndWait(Locator.linkWithText("Originating Location Specimen Lists")); @@ -551,11 +525,11 @@ private void verifyNotificationEmails() @LogMethod private void verifyRequestCancel() { - clickProject(getProjectName()); + goToProjectHome(); clickFolder(getFolderName()); - waitAndClick(Locator.linkWithText("Specimen Requests")); - click(Locator.linkWithText("View Current Requests")); + waitAndClick(Locator.xpath("//span[text() = 'Specimen Requests']/../../a")); + waitAndClick(Locator.linkWithText("View Current Requests")); waitForElement(Locator.id("dataregion_SpecimenRequest")); clickButton("Details"); @@ -566,10 +540,11 @@ private void verifyRequestCancel() clickButton("Cancel Request", 0); assertAlert("Canceling will permanently delete this pending request. Continue?"); waitForText("No data to show."); - clickFolder(getStudyLabel()); - clickAndWait(Locator.linkWithText("Specimen Data")); - click(Locator.linkWithText("Vials by Derivative Type")); - waitAndClick(WAIT_FOR_JAVASCRIPT, Locator.linkWithText("Swab"), WAIT_FOR_PAGE); + clickTab("Specimen Data"); + waitForVialSearch(); + click(Locator.xpath("//span[text()='Vials by Derivative Type']/../img")); + waitForElement(Locator.linkWithText("Swab")); + clickAndWait(Locator.linkWithText("Swab")); checkCheckbox(".toggle"); clickMenuButton("Request Options", "Create New Request"); clickButton("Cancel"); @@ -579,9 +554,9 @@ private void verifyRequestCancel() private void verifyReports() { log("check reports by participant group"); - clickFolder(getFolderName()); - clickAndWait(Locator.linkWithText("Specimen Data")); - waitAndClick(WAIT_FOR_JAVASCRIPT, Locator.linkWithText("Blood (Whole)"), WAIT_FOR_PAGE); + clickTab("Specimen Data"); + waitForVialSearch(); + clickAndWait(Locator.linkWithText("Blood (Whole)")); pushLocation(); clickAndWait(Locator.linkWithText("Reports")); clickButton("View"); // Summary Report @@ -609,7 +584,7 @@ private void verifyReports() @LogMethod private void verifyRequestingLocationRestriction() { - clickProject(getProjectName()); + goToProjectHome(); clickFolder(getFolderName()); verifyRequestingLocationCounts(StudyLocationType.values()); // All locations should be enabled by default @@ -626,7 +601,7 @@ private void verifyRequestingLocationRestriction() @LogMethod private void verifySpecimenTableAttachments() { - clickProject(getProjectName()); + goToProjectHome(); clickFolder(getFolderName()); log("Setup Excel specimen attachment"); @@ -639,6 +614,7 @@ private void verifySpecimenTableAttachments() log("Create request with excel specimen attachment"); clickTab("Specimen Data"); + waitForVialSearch(); clickAndWait(Locator.linkWithText("Urine")); checkDataRegionCheckbox("SpecimenDetail", 0); _extHelper.clickMenuButton(true, "Request Options", "Create New Request"); @@ -659,6 +635,7 @@ private void verifySpecimenTableAttachments() log("Create request with text specimen attachment"); clickTab("Specimen Data"); + waitForVialSearch(); clickAndWait(Locator.linkWithText("Urine")); checkDataRegionCheckbox("SpecimenDetail", 1); _extHelper.clickMenuButton(true, "Request Options", "Create New Request"); @@ -696,11 +673,11 @@ private void verifyInactiveUsersInRequests() clickButton("Deactivate"); assertTextNotPresent(USER2); - clickProject(getProjectName()); + goToProjectHome(); clickFolder(getFolderName()); - waitAndClick(Locator.linkWithText("Specimen Requests")); - click(Locator.linkWithText("View Current Requests")); + waitAndClick(Locator.xpath("//span[text() = 'Specimen Requests']/../../a")); + waitAndClick(Locator.linkWithText("View Current Requests")); waitForElement(Locator.id("dataregion_SpecimenRequest")); clickButton("Details"); @@ -764,8 +741,8 @@ private void enableAndVerifyRequestingLocationTypes(@LoggedParam StudyLocationTy private void verifyRequestingLocationCounts(StudyLocationType... types) { clickTab("Specimen Data"); - click(Locator.linkWithText("Specimen Requests")); - click(Locator.linkWithText("Create New Request")); + waitForVialSearch(); + click(Locator.imageWithAltText("New Request Icon", true)); waitForElement(Locator.id("labkey-nav-trail-current-page").withText("New Specimen Request")); int expectedLocationCount = StudyLocationType.untypedSites(); @@ -794,7 +771,7 @@ private void verifyRequestingLocationCounts(StudyLocationType... types) @LogMethod private void verifySpecimenGroupings() { - clickProject(getProjectName()); + goToProjectHome(); clickFolder(getFolderName()); clickTab("Manage"); waitAndClick(Locator.linkWithText("Configure Specimen Groupings")); @@ -808,7 +785,7 @@ private void verifySpecimenGroupings() clickButton("Save"); waitForElement(Locator.id("labkey-nav-trail-current-page").withText("Manage Study")); clickTab("Specimen Data"); - waitForElement(Locator.linkWithText("Vials by Processing Location")); + waitForVialSearch(); assertTextPresent("Vials by Processing Location", "Vials by Additive Type", "The McMichael Lab"); assertTextPresent("NICD - Joberg", 2); clickAndWait(Locator.linkContainingText("The McMichael Lab, Oxford")); @@ -887,11 +864,6 @@ private void searchTest() assertTextPresent("IS NOT ANY OF "); } - private void waitForVialSearch() - { - waitForElement(Locator.css(".specimenSearchLoaded")); - } - @LogMethod private void exportSpecimenTest() { From 9b396fbdd5d1289ffd8db9766b0d863a7a2f9ec5 Mon Sep 17 00:00:00 2001 From: Cory Nathe Date: Thu, 27 Jun 2013 15:08:58 +0000 Subject: [PATCH 111/494] Automated test for specimen settings import/export SVN r27244 |2013-06-27 15:08:58 +0000 --- .../labkey/test/tests/SpecimenExportTest.java | 181 ++++++++++++++++++ 1 file changed, 181 insertions(+) create mode 100644 src/org/labkey/test/tests/SpecimenExportTest.java diff --git a/src/org/labkey/test/tests/SpecimenExportTest.java b/src/org/labkey/test/tests/SpecimenExportTest.java new file mode 100644 index 00000000..a4d398be --- /dev/null +++ b/src/org/labkey/test/tests/SpecimenExportTest.java @@ -0,0 +1,181 @@ +package org.labkey.test.tests; + +import junit.framework.Assert; +import org.labkey.test.Locator; +import org.labkey.test.TestTimeoutException; +import org.labkey.test.util.LogMethod; +import org.openqa.selenium.support.ui.ExpectedConditions; + +/** + * User: cnathe + * Date: 6/26/13 + */ +public class SpecimenExportTest extends SpecimenBaseTest +{ + protected static final String PROJECT_NAME = "SpecimenExportVerifyProject"; + + @Override + protected String getProjectName() + { + return PROJECT_NAME; + } + + @Override + protected boolean isFileUploadTest() + { + return true; + } + + @Override + protected BrowserType bestBrowser() + { + return BrowserType.CHROME; + } + + @Override + @LogMethod(category = LogMethod.MethodType.SETUP) + protected void doCreateSteps() + { + enableEmailRecorder(); + initializeFolder(); + + clickButton("Create Study"); + setFormElement(Locator.name("label"), getStudyLabel()); + click(Locator.radioButtonByNameAndValue("simpleRepository", "false")); + clickButton("Create Study"); + + setPipelineRoot(getPipelinePath()); + startSpecimenImport(1); + waitForSpecimenImport(); + setPipelineRootToDefault(); + + setupRepositoryType(); + setupRequestStatuses(); + setupActorsAndGroups(); + setupDefaultRequirements(); + setupWebpartGroupings(); + setupLocationTypes(); + // UNDONE: enable this once settings included in study archive - setupRequestabilityRules(); + // UNDONE: enable this once settings included in study archive - setupRequestForm(); + // UNDONE: enable this once settings included in study archive - setupActorNotification(); + } + + @Override + @LogMethod(category = LogMethod.MethodType.VERIFICATION) + protected void doVerifySteps() + { + // verify the specimen settings roundtrip by 1. exporting folder zip, 2. deleting the study, + // 3. importing folder zip, 4. exporting folder expanded, and verifying settings in the XML (instead of in the UI) + exportStudy(true, true); + deleteStudy(); + importFromZipExport(); + exportStudy(true, false); + verifySpecimenSettingsInArchive(); + } + + private void setupRepositoryType() + { + log("Setup specimen repository type settings"); + clickTab("Manage"); + clickAndWait(Locator.linkWithText("Change Repository Type")); + waitForElement(Locator.id("labkey-nav-trail-current-page").withText("Manage Repository Settings")); + checkRadioButton(Locator.radioButtonByName("simple").index(1)); // Advanced repository type + checkRadioButton(Locator.radioButtonByName("specimenDataEditable").index(1)); // Editable specimen data + checkRadioButton(Locator.radioButtonByName("enableRequests").index(0)); // Enabled specimen requests + clickButton("Submit"); + } + + private void setupWebpartGroupings() + { + log("Setup specimen webpart groupings"); + clickTab("Manage"); + clickAndWait(Locator.linkWithText("Configure Specimen Groupings")); + waitForElement(Locator.id("labkey-nav-trail-current-page").withText("Configure Specimen Web Part")); + _ext4Helper.selectComboBoxItemById("combo11", "Processing Location"); + _ext4Helper.selectComboBoxItemById("combo12", "Processing Location"); + _ext4Helper.selectComboBoxItemById("combo13", "Processing Location"); + _ext4Helper.selectComboBoxItemById("combo21", "Tube Type"); + _ext4Helper.selectComboBoxItemById("combo22", "Tube Type"); + _ext4Helper.selectComboBoxItemById("combo23", "Tube Type"); + clickButton("Save"); + } + + private void setupLocationTypes() + { + log("Setup location types"); + clickTab("Manage"); + clickAndWait(Locator.linkWithText("Manage Location Types")); + waitForElement(Locator.id("labkey-nav-trail-current-page").withText("Manage Location Types")); + _ext4Helper.checkCheckbox("Repository"); + _ext4Helper.uncheckCheckbox("Clinic"); + _ext4Helper.uncheckCheckbox("Site Affiliated Lab"); + _ext4Helper.checkCheckbox("Endpoint Lab"); + clickButton("Save"); + } + + private void importFromZipExport() + { + log("Import folder zip archive from pipeline"); + goToFolderManagement(); + clickAndWait(Locator.linkWithText("Import")); + clickButtonContainingText("Import Folder Using Pipeline"); + _extHelper.expandFileBrowserRootNode(); + _extHelper.selectFileBrowserItem("export/"); + _extHelper.selectAllFileBrowserFiles(); + selectImportDataAction("Import Folder"); + waitForPipelineJobsToComplete(2, "Folder import", false); + } + + private void verifySpecimenSettingsInArchive() + { + log("verify specimen settings in study.xml"); + _extHelper.selectFileBrowserItem("export/study/study.xml"); + doubleClick(Locator.xpath("//div[contains(@class, 'x-grid3-cell-inner') and text()='study.xml']")); + waitForText(""); + + log("verify specimen_settings.xml"); + clickAndWait(Locator.linkWithText("Manage Files")); + click(Locator.css("button.iconFolderTree")); + _shortWait.until(ExpectedConditions.visibilityOf(Locator.xpath("id('fileBrowser')//div[contains(@id, 'xsplit')]").findElement(_driver))); + _extHelper.selectFileBrowserItem("export/study/specimens/specimen_settings.xml"); + doubleClick(Locator.xpath("//div[contains(@class, 'x-grid3-cell-inner') and text()='specimen_settings.xml']")); + waitForText("", + "Processing Location", + "Tube Type", + ""); + assertTextPresentInThisOrder( + "", + "", + "", + "", + "", + ""); + assertTextPresentInThisOrder( + "", + "", + "", + "", + "", + ""); + assertTextPresentInThisOrder( + "", + "", + "", + "", + "", + "", + "", + ""); + assertTextPresentInThisOrder( + "", + "Originating IRB Approval", + "Providing IRB Approval", + "Receiving IRB Approval", + "", + "SLG Approval", + " Date: Thu, 27 Jun 2013 22:42:47 +0000 Subject: [PATCH 112/494] Editable Specimens feature. This fixes the Locked While Processing requestability rule, adds some testing for Editable to Aliquot test and fixes an issue with the "Requestable" ternary boolean (although a better fix is desired). SVN r27266 |2013-06-27 22:42:47 +0000 --- src/org/labkey/test/tests/AliquotTest.java | 144 ++++++++++++++++++--- 1 file changed, 128 insertions(+), 16 deletions(-) diff --git a/src/org/labkey/test/tests/AliquotTest.java b/src/org/labkey/test/tests/AliquotTest.java index 676f6fb2..9976f8d4 100644 --- a/src/org/labkey/test/tests/AliquotTest.java +++ b/src/org/labkey/test/tests/AliquotTest.java @@ -77,6 +77,7 @@ protected void doCreateSteps() protected void doVerifySteps() { createRequests(); + verifyEditableSpecimens(); } @Override @@ -122,7 +123,13 @@ protected void setupRequestabilityRules() public static final String ALIQUOT_ONE = "AAA07XK5-03"; public static final String ALIQUOT_TWO = "EBG002K4-25"; + public static final String ALIQUOT_THREE = "AAA07XK5-01"; + public static final String ALIQUOT_FOUR = "AAA07XK5-04"; public static final String ALIQUOT_ONE_CHECKBOX = "//input[@id='check_" + ALIQUOT_ONE + "']"; + public static final String ALIQUOT_ONE_EDITLINK = "//input[@id='check_" + ALIQUOT_ONE + "']/../../td/a[contains(text(), 'edit')]"; + public static final String ALIQUOT_THREE_CHECKBOX = "//input[@id='check_" + ALIQUOT_THREE + "']"; + public static final String ALIQUOT_FOUR_CHECKBOX = "//input[@id='check_" + ALIQUOT_FOUR + "']"; + public static final String ALIQUOT_FOUR_EDITLINK = "//input[@id='check_" + ALIQUOT_FOUR + "']/../../td/a[contains(text(), 'edit')]"; public static final String ALIQUOT_ONE_CHECKBOX_DISABLED = "//input[@id='check_" + ALIQUOT_ONE + "' and @disabled]"; public static final String UNAVAILABLE_ALIQUOT = "AAQ00032-02"; public static final String ALIQUOT_TWO_CHECKBOX = "//input[@id='check_" + ALIQUOT_TWO + "']"; @@ -139,14 +146,7 @@ private void createRequests() assertElementPresent(Locator.xpath(ALIQUOT_ONE_CHECKBOX)); checkCheckbox(Locator.xpath(ALIQUOT_ONE_CHECKBOX)); - _extHelper.clickMenuButton("Request Options", "Create New Request"); - selectOptionByText(Locator.name("destinationLocation"), DESTINATION_SITE); - setFormElement(Locator.id("input0"), "Assay Plan"); - setFormElement(Locator.id("input2"), "Comments"); - setFormElement(Locator.id("input1"), "Shipping"); - setFormElement(Locator.id("input3"), "sample last one input"); - clickButton("Create and View Details"); - assertTextPresent(DESTINATION_SITE); + createNewRequestFromQueryView(); assertTextPresent(ALIQUOT_ONE); assertTextNotPresent("Complete"); @@ -175,14 +175,7 @@ private void createRequests() assertElementPresent(Locator.xpath(ALIQUOT_ONE_CHECKBOX)); checkCheckbox(Locator.xpath(ALIQUOT_ONE_CHECKBOX)); - _extHelper.clickMenuButton("Request Options", "Create New Request"); - selectOptionByText(Locator.name("destinationLocation"), DESTINATION_SITE); - setFormElement(Locator.id("input0"), "Assay Plan"); - setFormElement(Locator.id("input2"), "Comments"); - setFormElement(Locator.id("input1"), "Shipping"); - setFormElement(Locator.id("input3"), "sample last one input"); - clickButton("Create and View Details"); - assertTextPresent(DESTINATION_SITE); + createNewRequestFromQueryView(); assertTextPresent(ALIQUOT_ONE); assertTextNotPresent("Complete"); @@ -201,4 +194,123 @@ private void createRequests() assertTextPresent(ALIQUOT_ONE); assertTextPresent(ALIQUOT_TWO); } + + @LogMethod + private void verifyEditableSpecimens() + { + // Change repository to editable + clickFolder(getStudyLabel()); + clickAndWait(Locator.linkWithText("Manage")); + clickAndWait(Locator.linkWithText("Change Repository Type")); + Locator enableEditableSpecimens = Locator.radioButtonByNameAndValue("specimenDataEditable", "true"); + waitForElement(enableEditableSpecimens); + checkRadioButton(enableEditableSpecimens); + clickButton("Submit"); + + verifyDeletingSpecimens(); + verifyEditingSpecimens(); + verifyInsertingSpecimens(); +// verifyIllegalImporting(); +// verifyLegalImporting(); + } + + @LogMethod + private void verifyDeletingSpecimens() + { + clickAndWait(Locator.linkWithText("Specimen Data")); + waitAndClickAndWait(Locator.linkWithText("Blood (Whole)")); + + // Check that ALIQUOT_ONE cannot be selected for Delete + assertElementPresent(Locator.xpath(ALIQUOT_ONE_CHECKBOX_DISABLED)); + + // There's a request from createRequest(); Submit it and make it completed to free up aliqouts + _extHelper.clickMenuButton("Request Options", "View Existing Requests"); + clickButton("Submit", 0); + assertAlert("Once a request is submitted, its specimen list may no longer be modified. Continue?"); + waitForElement(Locator.css("h3").withText("Your request has been successfully submitted.")); + clickAndWait(Locator.linkWithText("Update Request")); + selectOptionByText(Locator.name("status"), "Completed"); + clickButton("Save Changes and Send Notifications"); + + // Now try to delete ALIQUOT_ONE + clickFolder(getStudyLabel()); + clickAndWait(Locator.linkWithText("Specimen Data")); + waitAndClickAndWait(Locator.linkWithText("Blood (Whole)")); + assertElementPresent(Locator.xpath(ALIQUOT_ONE_CHECKBOX)); + checkCheckbox(Locator.xpath(ALIQUOT_ONE_CHECKBOX)); + click(Locator.linkWithText("Delete")); + assertAlert("Are you sure you want to delete the selected row?"); + waitForText("Specimen may not be deleted because it has been used in a request."); + clickButton("Back"); + + // Now delete a different aliquot + checkCheckbox(Locator.xpath(ALIQUOT_THREE_CHECKBOX)); + click(Locator.linkWithText("Delete")); + assertAlert("Are you sure you want to delete the selected row?"); + waitForElementToDisappear(Locator.xpath(ALIQUOT_THREE_CHECKBOX)); + } + + @LogMethod + private void verifyEditingSpecimens() + { + clickFolder(getStudyLabel()); + clickAndWait(Locator.linkWithText("Specimen Data")); + waitAndClickAndWait(Locator.linkWithText("Blood (Whole)")); + + // Create request with ALIQUOT_FOUR + checkCheckbox(Locator.xpath(ALIQUOT_FOUR_CHECKBOX)); + createNewRequestFromQueryView(); + assertTextPresent(ALIQUOT_FOUR); + + // Attempt to edit, which should be error + clickAndWait(Locator.linkWithText("Specimen Data")); + waitAndClickAndWait(Locator.linkWithText("Blood (Whole)")); + clickAndWait(Locator.xpath(ALIQUOT_FOUR_EDITLINK)); + assertTextNotPresent("Specimen may not be edited when it's in a non-final request."); + clickButton("Submit"); + waitForText("Specimen may not be edited when it's in a non-final request."); + clickButton("Cancel"); + + // Edit another specimen + waitAndClickAndWait(Locator.xpath(ALIQUOT_ONE_EDITLINK)); + assertTextNotPresent("Specimen may not be edited when it's in a non-final request."); + setFormElement(Locator.xpath("//input[@name='quf_VisitDescription']"), "VisitVisit"); + clickButton("Submit"); + waitForText("VisitVisit"); + } + + @LogMethod + private void verifyInsertingSpecimens() + { + + } + + protected static final String SPECIMEN_ARCHIVE_148 = getStudySampleDataPath() + "specimens/lab148.specimens"; + + @LogMethod + private void verifyIllegalImporting() + { + clickFolder(getStudyLabel()); // TODO: this doesn't work as is yet + startSpecimenImport(1, SPECIMEN_ARCHIVE_148); + setExpectSpecimenImportError(true); + waitForSpecimenImport(); + } + + @LogMethod + private void verifyLegalImporting() + { + + } + + private void createNewRequestFromQueryView() + { + _extHelper.clickMenuButton("Request Options", "Create New Request"); + selectOptionByText(Locator.name("destinationLocation"), DESTINATION_SITE); + setFormElement(Locator.id("input0"), "Assay Plan"); + setFormElement(Locator.id("input2"), "Comments"); + setFormElement(Locator.id("input1"), "Shipping"); + setFormElement(Locator.id("input3"), "sample last one input"); + clickButton("Create and View Details"); + assertTextPresent(DESTINATION_SITE); + } } From 38fcd55f11249638899c9ef3da352d18dd47f73c Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Tue, 2 Jul 2013 21:05:21 +0000 Subject: [PATCH 113/494] Update licenses SVN r27325 |2013-07-02 21:05:21 +0000 --- src/org/labkey/test/tests/SpecimenExportTest.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/org/labkey/test/tests/SpecimenExportTest.java b/src/org/labkey/test/tests/SpecimenExportTest.java index a4d398be..31d85080 100644 --- a/src/org/labkey/test/tests/SpecimenExportTest.java +++ b/src/org/labkey/test/tests/SpecimenExportTest.java @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2013 LabKey Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package org.labkey.test.tests; import junit.framework.Assert; From 5335ae6f01e947738100d4864c49c1a995292908 Mon Sep 17 00:00:00 2001 From: Cory Nathe Date: Wed, 3 Jul 2013 15:05:24 +0000 Subject: [PATCH 114/494] Fix SpecimenExportTest - the selectAllFileBrowserFiles helper works in Chrome but not Firefox SVN r27339 |2013-07-03 15:05:24 +0000 --- src/org/labkey/test/tests/SpecimenExportTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/org/labkey/test/tests/SpecimenExportTest.java b/src/org/labkey/test/tests/SpecimenExportTest.java index 31d85080..c0143d56 100644 --- a/src/org/labkey/test/tests/SpecimenExportTest.java +++ b/src/org/labkey/test/tests/SpecimenExportTest.java @@ -136,7 +136,7 @@ private void importFromZipExport() clickButtonContainingText("Import Folder Using Pipeline"); _extHelper.expandFileBrowserRootNode(); _extHelper.selectFileBrowserItem("export/"); - _extHelper.selectAllFileBrowserFiles(); + click(Locator.xpath("//div[contains(@class, 'x-grid3-cell-inner') and starts-with(text(), 'My Study_')]")); selectImportDataAction("Import Folder"); waitForPipelineJobsToComplete(2, "Folder import", false); } From 0196a38d5d4d9911fc2981f692dc6746083473f6 Mon Sep 17 00:00:00 2001 From: Greg Taylor Date: Tue, 9 Jul 2013 16:41:07 +0000 Subject: [PATCH 115/494] Changes to AliquotTest and a new test SpecimenMultipleImportTest to support recent work on specimen insert / delete / modify / and import. SVN r27382 |2013-07-09 16:41:07 +0000 --- src/org/labkey/test/tests/AliquotTest.java | 51 ++++++---- .../tests/SpecimenMultipleImportTest.java | 93 +++++++++++++++++++ 2 files changed, 124 insertions(+), 20 deletions(-) create mode 100644 src/org/labkey/test/tests/SpecimenMultipleImportTest.java diff --git a/src/org/labkey/test/tests/AliquotTest.java b/src/org/labkey/test/tests/AliquotTest.java index 9976f8d4..b3ce2608 100644 --- a/src/org/labkey/test/tests/AliquotTest.java +++ b/src/org/labkey/test/tests/AliquotTest.java @@ -27,6 +27,7 @@ public class AliquotTest extends SpecimenBaseTest { protected static final String PROJECT_NAME = "AliquotVerifyProject"; + protected static final String SPECIMEN_ARCHIVE_148 = getStudySampleDataPath() + "specimens/lab148.specimens"; @Override protected String getProjectName() @@ -69,7 +70,6 @@ protected void doCreateSteps() setupDefaultRequirements(); setupRequestForm(); setupActorNotification(); - } @Override @@ -199,7 +199,7 @@ private void createRequests() private void verifyEditableSpecimens() { // Change repository to editable - clickFolder(getStudyLabel()); + clickFolder(getFolderName()); clickAndWait(Locator.linkWithText("Manage")); clickAndWait(Locator.linkWithText("Change Repository Type")); Locator enableEditableSpecimens = Locator.radioButtonByNameAndValue("specimenDataEditable", "true"); @@ -210,15 +210,15 @@ private void verifyEditableSpecimens() verifyDeletingSpecimens(); verifyEditingSpecimens(); verifyInsertingSpecimens(); -// verifyIllegalImporting(); -// verifyLegalImporting(); + + verifyIllegalImporting(); } @LogMethod private void verifyDeletingSpecimens() { clickAndWait(Locator.linkWithText("Specimen Data")); - waitAndClickAndWait(Locator.linkWithText("Blood (Whole)")); + waitAndClickAndWait(Locator.linkWithText("Blood (Whole)").notHidden()); // Check that ALIQUOT_ONE cannot be selected for Delete assertElementPresent(Locator.xpath(ALIQUOT_ONE_CHECKBOX_DISABLED)); @@ -233,9 +233,9 @@ private void verifyDeletingSpecimens() clickButton("Save Changes and Send Notifications"); // Now try to delete ALIQUOT_ONE - clickFolder(getStudyLabel()); + clickFolder(getFolderName()); clickAndWait(Locator.linkWithText("Specimen Data")); - waitAndClickAndWait(Locator.linkWithText("Blood (Whole)")); + waitAndClickAndWait(Locator.linkWithText("Blood (Whole)").notHidden()); assertElementPresent(Locator.xpath(ALIQUOT_ONE_CHECKBOX)); checkCheckbox(Locator.xpath(ALIQUOT_ONE_CHECKBOX)); click(Locator.linkWithText("Delete")); @@ -253,9 +253,9 @@ private void verifyDeletingSpecimens() @LogMethod private void verifyEditingSpecimens() { - clickFolder(getStudyLabel()); + clickFolder(getFolderName()); clickAndWait(Locator.linkWithText("Specimen Data")); - waitAndClickAndWait(Locator.linkWithText("Blood (Whole)")); + waitAndClickAndWait(Locator.linkWithText("Blood (Whole)").notHidden()); // Create request with ALIQUOT_FOUR checkCheckbox(Locator.xpath(ALIQUOT_FOUR_CHECKBOX)); @@ -264,7 +264,7 @@ private void verifyEditingSpecimens() // Attempt to edit, which should be error clickAndWait(Locator.linkWithText("Specimen Data")); - waitAndClickAndWait(Locator.linkWithText("Blood (Whole)")); + waitAndClickAndWait(Locator.linkWithText("Blood (Whole)").notHidden()); clickAndWait(Locator.xpath(ALIQUOT_FOUR_EDITLINK)); assertTextNotPresent("Specimen may not be edited when it's in a non-final request."); clickButton("Submit"); @@ -282,24 +282,35 @@ private void verifyEditingSpecimens() @LogMethod private void verifyInsertingSpecimens() { - + clickFolder(getFolderName()); + clickAndWait(Locator.linkWithText("Specimen Data")); + waitAndClickAndWait(Locator.linkWithText("Blood (Whole)").notHidden()); + + // verify insert new here + clickAndWait(Locator.linkWithText("Insert New")); + setFormElement(Locator.xpath("//input[@name='quf_GlobalUniqueId']"), "Global"); + setFormElement(Locator.xpath("//input[@name='quf_VisitDescription']"), "NewVisit"); + setFormElement(Locator.xpath("//input[@name='quf_SequenceNum']"), "001"); + setFormElement(Locator.name("quf_ParticipantId"), "618005775"); + clickButton("Submit"); + assertTextNotPresent("Error"); + setFilter("SpecimenDetail", "VisitDescription", "Equals", "NewVisit"); + assertTextPresent("NewVisit"); } - protected static final String SPECIMEN_ARCHIVE_148 = getStudySampleDataPath() + "specimens/lab148.specimens"; - @LogMethod private void verifyIllegalImporting() { - clickFolder(getStudyLabel()); // TODO: this doesn't work as is yet - startSpecimenImport(1, SPECIMEN_ARCHIVE_148); + checkErrors(); + clickProject("AliquotVerifyProject"); + + clickFolder(getFolderName()); + startSpecimenImport(2, SPECIMEN_ARCHIVE_148); setExpectSpecimenImportError(true); waitForSpecimenImport(); - } - - @LogMethod - private void verifyLegalImporting() - { + // Make sure the expected errors have been logged and will not hang up the test later on. + checkExpectedErrors(1); } private void createNewRequestFromQueryView() diff --git a/src/org/labkey/test/tests/SpecimenMultipleImportTest.java b/src/org/labkey/test/tests/SpecimenMultipleImportTest.java new file mode 100644 index 00000000..ac6e06de --- /dev/null +++ b/src/org/labkey/test/tests/SpecimenMultipleImportTest.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2013 LabKey Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.labkey.test.tests; + +import org.labkey.test.Locator; +import org.labkey.test.util.LogMethod; + +/** + * User: gktaylor + * Date: 7/8/13 + * Time: 1:44 PM + */ +public class SpecimenMultipleImportTest extends StudyBaseTestWD +{ + protected static final String PROJECT_NAME = "AliquotVerifyProject"; + + protected static final String SPECIMEN_ARCHIVE_15 = getStudySampleDataPath() + "specimens/lab15.specimens"; + protected static final String SPECIMEN_ARCHIVE_19 = getStudySampleDataPath() + "specimens/lab19.specimens"; + protected static final String SPECIMEN_ARCHIVE_20 = getStudySampleDataPath() + "specimens/lab20.specimens"; + protected static final String SPECIMEN_ARCHIVE_21 = getStudySampleDataPath() + "specimens/lab21.specimens"; + protected static final String SPECIMEN_ARCHIVE_22 = getStudySampleDataPath() + "specimens/lab22.specimens"; + + @Override + protected String getProjectName() + { + return PROJECT_NAME; + } + + @Override + protected boolean isFileUploadTest() + { + return true; + } + + @Override + protected BrowserType bestBrowser() + { + return BrowserType.CHROME; + } + + @Override + @LogMethod(category = LogMethod.MethodType.SETUP) + protected void doCreateSteps() + { + enableEmailRecorder(); + initializeFolder(); + + clickButton("Create Study"); + setFormElement(Locator.name("label"), getStudyLabel()); + click(Locator.radioButtonByNameAndValue("simpleRepository", "false")); + clickButton("Create Study"); + + setPipelineRoot(getPipelinePath()); + + startSpecimenImport(1, SPECIMEN_ARCHIVE_15); + startSpecimenImport(2, SPECIMEN_ARCHIVE_19); + startSpecimenImport(3, SPECIMEN_ARCHIVE_20); + startSpecimenImport(4, SPECIMEN_ARCHIVE_21); + startSpecimenImport(5, SPECIMEN_ARCHIVE_22); + + waitForSpecimenImport(); + } + + @Override + @LogMethod(category = LogMethod.MethodType.VERIFICATION) + protected void doVerifySteps() + { + clickFolder(getFolderName()); + clickAndWait(Locator.linkWithText("Specimen Data")); + waitAndClickAndWait(Locator.linkWithText("Blood (Whole)").notHidden()); + + // check for number of specimens in import, total volume is a good identifier for everything + assertTextPresent("75,990,002.5"); + + // look for a specimen that came from 1st and 3rd file + assertTextPresent("999320895"); + assertTextPresent("999320422"); + } +} From a90569782c7a6b688dca70a7ca0320aecf4f9f83 Mon Sep 17 00:00:00 2001 From: Trey Chadick Date: Mon, 29 Jul 2013 16:49:58 +0000 Subject: [PATCH 116/494] Allow tests to live in their associated modules' directories: Test compiler uses .modules file to find test sources Relocate some module tests (EHR, CDS, OconnorExperiments) Add module test directories as additional "Content Root"s in IntelliJ test module Specify test suites by Annotation: Create marker interfaces for all suites Add @Category notation to all tests with suites based on TestSet.java SVN r27644 |2013-07-29 16:49:58 +0000 --- src/org/labkey/test/tests/AliquotTest.java | 4 ++++ src/org/labkey/test/tests/SpecimenExportTest.java | 4 ++++ src/org/labkey/test/tests/SpecimenMultipleImportTest.java | 4 ++++ src/org/labkey/test/tests/SpecimenTest.java | 3 +++ 4 files changed, 15 insertions(+) diff --git a/src/org/labkey/test/tests/AliquotTest.java b/src/org/labkey/test/tests/AliquotTest.java index b3ce2608..af4611b3 100644 --- a/src/org/labkey/test/tests/AliquotTest.java +++ b/src/org/labkey/test/tests/AliquotTest.java @@ -16,7 +16,10 @@ package org.labkey.test.tests; +import org.junit.experimental.categories.Category; import org.labkey.test.Locator; +import org.labkey.test.categories.DailyA; +import org.labkey.test.categories.Specimen; import org.labkey.test.util.LogMethod; /** @@ -24,6 +27,7 @@ * Date: 5/14/13 * Time: 1:44 PM */ +@Category({DailyA.class, Specimen.class}) public class AliquotTest extends SpecimenBaseTest { protected static final String PROJECT_NAME = "AliquotVerifyProject"; diff --git a/src/org/labkey/test/tests/SpecimenExportTest.java b/src/org/labkey/test/tests/SpecimenExportTest.java index c0143d56..8b0947b4 100644 --- a/src/org/labkey/test/tests/SpecimenExportTest.java +++ b/src/org/labkey/test/tests/SpecimenExportTest.java @@ -16,8 +16,11 @@ package org.labkey.test.tests; import junit.framework.Assert; +import org.junit.experimental.categories.Category; import org.labkey.test.Locator; import org.labkey.test.TestTimeoutException; +import org.labkey.test.categories.DailyB; +import org.labkey.test.categories.Specimen; import org.labkey.test.util.LogMethod; import org.openqa.selenium.support.ui.ExpectedConditions; @@ -25,6 +28,7 @@ * User: cnathe * Date: 6/26/13 */ +@Category({DailyB.class, Specimen.class}) public class SpecimenExportTest extends SpecimenBaseTest { protected static final String PROJECT_NAME = "SpecimenExportVerifyProject"; diff --git a/src/org/labkey/test/tests/SpecimenMultipleImportTest.java b/src/org/labkey/test/tests/SpecimenMultipleImportTest.java index ac6e06de..1f89d3ba 100644 --- a/src/org/labkey/test/tests/SpecimenMultipleImportTest.java +++ b/src/org/labkey/test/tests/SpecimenMultipleImportTest.java @@ -16,7 +16,10 @@ package org.labkey.test.tests; +import org.junit.experimental.categories.Category; import org.labkey.test.Locator; +import org.labkey.test.categories.DailyA; +import org.labkey.test.categories.Specimen; import org.labkey.test.util.LogMethod; /** @@ -24,6 +27,7 @@ * Date: 7/8/13 * Time: 1:44 PM */ +@Category({DailyA.class, Specimen.class}) public class SpecimenMultipleImportTest extends StudyBaseTestWD { protected static final String PROJECT_NAME = "AliquotVerifyProject"; diff --git a/src/org/labkey/test/tests/SpecimenTest.java b/src/org/labkey/test/tests/SpecimenTest.java index 35e3d025..f02173c5 100644 --- a/src/org/labkey/test/tests/SpecimenTest.java +++ b/src/org/labkey/test/tests/SpecimenTest.java @@ -19,8 +19,10 @@ import org.apache.http.HttpException; import org.apache.http.HttpStatus; import org.junit.Assert; +import org.junit.experimental.categories.Category; import org.labkey.test.Locator; import org.labkey.test.WebTestHelper; +import org.labkey.test.categories.DailyA; import org.labkey.test.util.DataRegionTable; import org.labkey.test.util.LabKeyExpectedConditions; import org.labkey.test.util.LogMethod; @@ -40,6 +42,7 @@ * Date: Mar 9, 2006 * Time: 1:54:57 PM */ +@Category({DailyA.class}) public class SpecimenTest extends SpecimenBaseTest { protected static final String PROJECT_NAME = "SpecimenVerifyProject"; From 636c2c28e726578c141e803102038c92cfd1776f Mon Sep 17 00:00:00 2001 From: Trey Chadick Date: Tue, 20 Aug 2013 21:52:27 +0000 Subject: [PATCH 117/494] Wait for user form to fully render SVN r27942 |2013-08-20 21:52:27 +0000 --- src/org/labkey/test/tests/SpecimenBaseTest.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/org/labkey/test/tests/SpecimenBaseTest.java b/src/org/labkey/test/tests/SpecimenBaseTest.java index be284bc7..aabc91f5 100644 --- a/src/org/labkey/test/tests/SpecimenBaseTest.java +++ b/src/org/labkey/test/tests/SpecimenBaseTest.java @@ -89,6 +89,7 @@ protected void setupActorsAndGroups() selectOptionByText(Locator.name("newPerSite"), "One Per Study"); clickButton("Save"); clickAndWait(Locator.linkWithText("Update Members")); + waitForElement(Locator.name("names")); setFormElement(Locator.name("names"), USER1); uncheckCheckbox(Locator.checkboxByName("sendEmail")); clickButton("Update Members"); @@ -97,6 +98,7 @@ protected void setupActorsAndGroups() clickButton("Save"); clickAndWait(Locator.linkWithText("Update Members").index(1)); clickAndWait(Locator.linkWithText(DESTINATION_SITE)); + waitForElement(Locator.name("names")); setFormElement(Locator.name("names"), USER2); uncheckCheckbox(Locator.checkboxByName("sendEmail")); clickAndWait(Locator.linkWithText("Update Members")); From 6a6a51eb2625f1a16ed67274f94bf98ad6c98122 Mon Sep 17 00:00:00 2001 From: Trey Chadick Date: Thu, 5 Sep 2013 22:04:22 +0000 Subject: [PATCH 118/494] More accurate display name for Query export audit event type SVN r28208 |2013-09-05 22:04:22 +0000 --- src/org/labkey/test/tests/SpecimenTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/org/labkey/test/tests/SpecimenTest.java b/src/org/labkey/test/tests/SpecimenTest.java index f02173c5..68b161c1 100644 --- a/src/org/labkey/test/tests/SpecimenTest.java +++ b/src/org/labkey/test/tests/SpecimenTest.java @@ -877,7 +877,7 @@ private void exportSpecimenTest() goToAuditLog(); - selectOptionByText(Locator.name("view"), "Query events"); + selectOptionByText(Locator.name("view"), "Query export events"); waitForElement(Locator.id("dataregion_audit")); DataRegionTable auditTable = new DataRegionTable("audit", this); From 7ef227a576bbb895a12bf143b4c68ec18a764537 Mon Sep 17 00:00:00 2001 From: Kevin Krouse Date: Mon, 16 Sep 2013 21:56:57 +0000 Subject: [PATCH 119/494] finally perform the migrate audit to hard tables SVN r28401 |2013-09-16 21:56:57 +0000 --- src/org/labkey/test/tests/SpecimenTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/org/labkey/test/tests/SpecimenTest.java b/src/org/labkey/test/tests/SpecimenTest.java index 68b161c1..25228ed1 100644 --- a/src/org/labkey/test/tests/SpecimenTest.java +++ b/src/org/labkey/test/tests/SpecimenTest.java @@ -878,9 +878,9 @@ private void exportSpecimenTest() goToAuditLog(); selectOptionByText(Locator.name("view"), "Query export events"); - waitForElement(Locator.id("dataregion_audit")); + waitForElement(Locator.id("dataregion_query")); - DataRegionTable auditTable = new DataRegionTable("audit", this); + DataRegionTable auditTable = new DataRegionTable("query", this); String[][] columnAndValues = new String[][] {{"Created By", getDisplayName()}, {"Project", PROJECT_NAME}, {"Container", getFolderName()}, {"SchemaName", "study"}, {"QueryName", SPECIMEN_DETAIL}, {"Comment", "Exported to Excel Web Query data"}}; From ad1065bdfe53bf55cf3a13b9a35fe7b7d40bedb5 Mon Sep 17 00:00:00 2001 From: Rylan Bauermeister Date: Tue, 24 Sep 2013 17:15:13 +0000 Subject: [PATCH 120/494] Automated tests for table truncation buttons SVN r28493 |2013-09-24 17:15:13 +0000 --- .../labkey/test/tests/SpecimenImportTest.java | 144 ++++++++++++++++++ 1 file changed, 144 insertions(+) create mode 100644 src/org/labkey/test/tests/SpecimenImportTest.java diff --git a/src/org/labkey/test/tests/SpecimenImportTest.java b/src/org/labkey/test/tests/SpecimenImportTest.java new file mode 100644 index 00000000..5f5c37cd --- /dev/null +++ b/src/org/labkey/test/tests/SpecimenImportTest.java @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2007-2013 LabKey Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.labkey.test.tests; + +import org.apache.http.HttpStatus; +import org.junit.experimental.categories.Category; +import org.labkey.test.Locator; +import org.labkey.test.WebTestHelper; +import org.labkey.test.categories.DailyA; +import org.labkey.test.util.LogMethod; + +@Category({DailyA.class}) +public class SpecimenImportTest extends SpecimenBaseTest +{ + private final String noVisitIdTSV = + "Global Unique Id\tSample Id\tDraw Timestamp\tVisit\tParticipant Id\n" + + "1\t1\t\t\t1\n" + + "2\t2\t\t\t2\n" + + "3\t3\t\t\t\n" + + "4\t4\t\t\t"; + + private final String noPtidsTSV = + "Global Unique Id\tSample Id\tDraw Timestamp\tVisit\tParticipant\n" + + "\t\t\t1\t\n" + + "\t\t\t2\t\n" + + "\t\t\t3\t\n" + + "\t\t\t4\t"; + private final String cleanEntry = + "Global Unique Id\tSample Id\tDraw Timestamp\tVisit\tParticipant Id\n" + + "1\t1\t\t1\t1\n" + + "2\t2\t\t2\t2\n" + + "3\t3\t\t3\t\n" + + "4\t4\t\t4\t"; + + @Override + @LogMethod(category = LogMethod.MethodType.SETUP) + protected void doCreateSteps() + { + initializeFolder(); + + checkRequiredFields(false); + changeTimepointType(); + + waitForText("General Study Settings"); + checkRequiredFields(true); + doUploads(); + + assertIdsSet(); + } + + @Override + protected void doVerifySteps() + { + //Do Nothing + } + + protected void goToImport() + { + waitAndClick(Locator.linkContainingText("Data")); + waitAndClick(Locator.linkContainingText("Import Specimens")); + } + + protected void checkRequiredFields(boolean visit) + { + goToImport(); + waitAndClick(Locator.linkContainingText("Show Required Data Fields")); + assertTextPresent("Global Unique Id","Sample Id","Participant Id", "Volume", "Volume Units", "Primary Type", "Derivative Type", "Additive Type"); + if(!visit) + { + assertTextNotPresent("Visit"); + assertTextPresent("Draw Timestamp"); + } + else + { + assertTextPresent("Visit"); + assertTextNotPresent("Draw Timestamp"); + } + } + + protected void changeTimepointType() + { + click(Locator.linkContainingText("Manage")); + waitAndClick(Locator.linkContainingText("Change Study Properties")); + waitForText("Timepoint Type:"); + _ext4Helper.selectRadioButton("Timepoint Type:", "VISIT"); + click(Locator.ext4Button("Submit")); + } + protected void doUploads() + { + setFormElementJS(Locator.name("tsv"), noVisitIdTSV); + clickButton("Submit"); + waitForText("Error: row 1 does not contain a value for field Visit"); + setFormElementJS(Locator.name("tsv"), noPtidsTSV); + clickButton("Submit"); + waitForText("Error in row 1: required field Participant was not supplied"); + assertTextPresent("Error in row 1: missing Global Unique Id"); + setFormElementJS(Locator.name("tsv"), cleanEntry); + clickButton("Submit"); + waitForText("Specimens uploaded successfully."); + } + + protected void assertIdsSet() + { + click(Locator.linkContainingText("Data")); + waitAndClick(Locator.linkContainingText("By Individual Vial")); + assertTextAtPlaceInTable("1", "dataregion_SpecimenDetail", 5, 1); + assertTextAtPlaceInTable("1", "dataregion_SpecimenDetail", 5, 2); + assertTextAtPlaceInTable("3", "dataregion_SpecimenDetail", 7, 1); + assertTextAtPlaceInTable("3", "dataregion_SpecimenDetail", 7, 2); + } + + + @Override + protected void initializeFolder() + { + hoverProjectBar(); + int response = -1; + try{ + response = WebTestHelper.getHttpGetResponse(getBaseURL() + "/" + stripContextPath(getAttribute(Locator.linkWithText(getProjectName()), "href"))); + } + catch(Exception e){/*No link or bad response*/} + + if (HttpStatus.SC_OK != response) + { + _containerHelper.createProject(getProjectName(), null); + } + + createSubfolder(getProjectName(), getProjectName(), getFolderName(), "CAVD Study", null, true); + } +} From 009d0535e036616623704bf4ba07ec214d15da16 Mon Sep 17 00:00:00 2001 From: Trey Chadick Date: Wed, 25 Sep 2013 00:10:39 +0000 Subject: [PATCH 121/494] Automated refactor: Encapsulate _driver, _shortWait, and _longWait Move static Locators into new Locators class SVN r28513 |2013-09-25 00:10:39 +0000 --- src/org/labkey/test/tests/SpecimenExportTest.java | 4 +--- src/org/labkey/test/tests/SpecimenTest.java | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/org/labkey/test/tests/SpecimenExportTest.java b/src/org/labkey/test/tests/SpecimenExportTest.java index 8b0947b4..f19d578a 100644 --- a/src/org/labkey/test/tests/SpecimenExportTest.java +++ b/src/org/labkey/test/tests/SpecimenExportTest.java @@ -15,10 +15,8 @@ */ package org.labkey.test.tests; -import junit.framework.Assert; import org.junit.experimental.categories.Category; import org.labkey.test.Locator; -import org.labkey.test.TestTimeoutException; import org.labkey.test.categories.DailyB; import org.labkey.test.categories.Specimen; import org.labkey.test.util.LogMethod; @@ -155,7 +153,7 @@ private void verifySpecimenSettingsInArchive() log("verify specimen_settings.xml"); clickAndWait(Locator.linkWithText("Manage Files")); click(Locator.css("button.iconFolderTree")); - _shortWait.until(ExpectedConditions.visibilityOf(Locator.xpath("id('fileBrowser')//div[contains(@id, 'xsplit')]").findElement(_driver))); + shortWait().until(ExpectedConditions.visibilityOf(Locator.xpath("id('fileBrowser')//div[contains(@id, 'xsplit')]").findElement(getDriver()))); _extHelper.selectFileBrowserItem("export/study/specimens/specimen_settings.xml"); doubleClick(Locator.xpath("//div[contains(@class, 'x-grid3-cell-inner') and text()='specimen_settings.xml']")); waitForText(" Date: Wed, 25 Sep 2013 16:54:22 +0000 Subject: [PATCH 122/494] 18654: fixes to expected data fields table for specimen import. SVN r28521 |2013-09-25 16:54:22 +0000 --- src/org/labkey/test/tests/SpecimenImportTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/org/labkey/test/tests/SpecimenImportTest.java b/src/org/labkey/test/tests/SpecimenImportTest.java index 5f5c37cd..dc24e404 100644 --- a/src/org/labkey/test/tests/SpecimenImportTest.java +++ b/src/org/labkey/test/tests/SpecimenImportTest.java @@ -87,7 +87,6 @@ protected void checkRequiredFields(boolean visit) else { assertTextPresent("Visit"); - assertTextNotPresent("Draw Timestamp"); } } From f2f0235ae3e898f609cc3e68388336300e4924cf Mon Sep 17 00:00:00 2001 From: Rylan Bauermeister Date: Thu, 26 Sep 2013 00:40:55 +0000 Subject: [PATCH 123/494] Backwards compatibility for Specimen Import, test fixes for Specimen Import. SVN r28535 |2013-09-26 00:40:55 +0000 --- src/org/labkey/test/tests/SpecimenImportTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/org/labkey/test/tests/SpecimenImportTest.java b/src/org/labkey/test/tests/SpecimenImportTest.java index dc24e404..ab36b2c0 100644 --- a/src/org/labkey/test/tests/SpecimenImportTest.java +++ b/src/org/labkey/test/tests/SpecimenImportTest.java @@ -77,7 +77,7 @@ protected void goToImport() protected void checkRequiredFields(boolean visit) { goToImport(); - waitAndClick(Locator.linkContainingText("Show Required Data Fields")); + waitAndClick(Locator.linkContainingText("Show Expected Data Fields")); assertTextPresent("Global Unique Id","Sample Id","Participant Id", "Volume", "Volume Units", "Primary Type", "Derivative Type", "Additive Type"); if(!visit) { From 780bab8604e078a2d02f210bf68e5bbe5eb3d7bd Mon Sep 17 00:00:00 2001 From: Rylan Bauermeister Date: Thu, 26 Sep 2013 20:50:32 +0000 Subject: [PATCH 124/494] Test fix for SpecimenImportTest SVN r28549 |2013-09-26 20:50:32 +0000 --- src/org/labkey/test/tests/AliquotTest.java | 8 ++++++++ src/org/labkey/test/tests/SpecimenBaseTest.java | 1 - src/org/labkey/test/tests/SpecimenExportTest.java | 8 ++++++++ src/org/labkey/test/tests/SpecimenTest.java | 8 ++++++++ 4 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/org/labkey/test/tests/AliquotTest.java b/src/org/labkey/test/tests/AliquotTest.java index af4611b3..72f7edaa 100644 --- a/src/org/labkey/test/tests/AliquotTest.java +++ b/src/org/labkey/test/tests/AliquotTest.java @@ -18,6 +18,7 @@ import org.junit.experimental.categories.Category; import org.labkey.test.Locator; +import org.labkey.test.TestTimeoutException; import org.labkey.test.categories.DailyA; import org.labkey.test.categories.Specimen; import org.labkey.test.util.LogMethod; @@ -39,6 +40,13 @@ protected String getProjectName() return PROJECT_NAME; } + @Override + protected void doCleanup(boolean afterTest) throws TestTimeoutException + { + deleteUsers(afterTest, USER1, USER2); + super.doCleanup(afterTest); + } + @Override protected boolean isFileUploadTest() { diff --git a/src/org/labkey/test/tests/SpecimenBaseTest.java b/src/org/labkey/test/tests/SpecimenBaseTest.java index aabc91f5..856d8acc 100644 --- a/src/org/labkey/test/tests/SpecimenBaseTest.java +++ b/src/org/labkey/test/tests/SpecimenBaseTest.java @@ -41,7 +41,6 @@ public abstract class SpecimenBaseTest extends StudyBaseTestWD @Override protected void doCleanup(boolean afterTest) throws TestTimeoutException { - deleteUsers(afterTest, USER1, USER2); super.doCleanup(afterTest); } diff --git a/src/org/labkey/test/tests/SpecimenExportTest.java b/src/org/labkey/test/tests/SpecimenExportTest.java index f19d578a..0e4f7f95 100644 --- a/src/org/labkey/test/tests/SpecimenExportTest.java +++ b/src/org/labkey/test/tests/SpecimenExportTest.java @@ -17,6 +17,7 @@ import org.junit.experimental.categories.Category; import org.labkey.test.Locator; +import org.labkey.test.TestTimeoutException; import org.labkey.test.categories.DailyB; import org.labkey.test.categories.Specimen; import org.labkey.test.util.LogMethod; @@ -37,6 +38,13 @@ protected String getProjectName() return PROJECT_NAME; } + @Override + protected void doCleanup(boolean afterTest) throws TestTimeoutException + { + deleteUsers(afterTest, USER1, USER2); + super.doCleanup(afterTest); + } + @Override protected boolean isFileUploadTest() { diff --git a/src/org/labkey/test/tests/SpecimenTest.java b/src/org/labkey/test/tests/SpecimenTest.java index ac9dd8df..194d9780 100644 --- a/src/org/labkey/test/tests/SpecimenTest.java +++ b/src/org/labkey/test/tests/SpecimenTest.java @@ -21,6 +21,7 @@ import org.junit.Assert; import org.junit.experimental.categories.Category; import org.labkey.test.Locator; +import org.labkey.test.TestTimeoutException; import org.labkey.test.WebTestHelper; import org.labkey.test.categories.DailyA; import org.labkey.test.util.DataRegionTable; @@ -60,6 +61,13 @@ protected String getProjectName() return PROJECT_NAME; } + @Override + protected void doCleanup(boolean afterTest) throws TestTimeoutException + { + deleteUsers(afterTest, USER1, USER2); + super.doCleanup(afterTest); + } + @Override protected boolean isFileUploadTest() { From a023a67dafeeac0e6a49fef6ee387ee94aff0ada Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Thu, 3 Oct 2013 17:41:12 +0000 Subject: [PATCH 125/494] Update licenses SVN r28679 |2013-10-03 17:41:12 +0000 --- src/org/labkey/test/tests/SpecimenImportTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/org/labkey/test/tests/SpecimenImportTest.java b/src/org/labkey/test/tests/SpecimenImportTest.java index ab36b2c0..4f99f551 100644 --- a/src/org/labkey/test/tests/SpecimenImportTest.java +++ b/src/org/labkey/test/tests/SpecimenImportTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007-2013 LabKey Corporation + * Copyright (c) 2013 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. From df5a9988ab83293faaedaa9470f004cef6ea4b47 Mon Sep 17 00:00:00 2001 From: Trey Chadick Date: Fri, 4 Oct 2013 17:11:36 +0000 Subject: [PATCH 126/494] Wait for page loads SVN r28711 |2013-10-04 17:11:36 +0000 --- src/org/labkey/test/tests/SpecimenImportTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/org/labkey/test/tests/SpecimenImportTest.java b/src/org/labkey/test/tests/SpecimenImportTest.java index 4f99f551..51ab1634 100644 --- a/src/org/labkey/test/tests/SpecimenImportTest.java +++ b/src/org/labkey/test/tests/SpecimenImportTest.java @@ -70,8 +70,8 @@ protected void doVerifySteps() protected void goToImport() { - waitAndClick(Locator.linkContainingText("Data")); - waitAndClick(Locator.linkContainingText("Import Specimens")); + waitAndClickAndWait(Locator.linkContainingText("Data")); + waitAndClickAndWait(Locator.linkContainingText("Import Specimens")); } protected void checkRequiredFields(boolean visit) From 57d6950ac094d522d1b02a918a5039440c30db13 Mon Sep 17 00:00:00 2001 From: Trey Chadick Date: Tue, 15 Oct 2013 16:36:22 +0000 Subject: [PATCH 127/494] Wait for link SVN r28894 |2013-10-15 16:36:22 +0000 --- src/org/labkey/test/tests/SpecimenTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/org/labkey/test/tests/SpecimenTest.java b/src/org/labkey/test/tests/SpecimenTest.java index 194d9780..226c4677 100644 --- a/src/org/labkey/test/tests/SpecimenTest.java +++ b/src/org/labkey/test/tests/SpecimenTest.java @@ -157,6 +157,7 @@ private void verifyRequestEnabled() private void verifyRequestsDisabled() { clickFolder(getFolderName()); + waitForElement(Locator.linkWithText("By Individual Vial")); assertTextNotPresent("Specimen Requests"); clickAndWait(Locator.linkWithText("By Individual Vial")); assertElementNotPresent(Locator.navButton("Request Options")); From ceac039e7365d19d40e3b7f94f5acfa7864b2e5e Mon Sep 17 00:00:00 2001 From: Rylan Bauermeister Date: Tue, 22 Oct 2013 23:45:14 +0000 Subject: [PATCH 128/494] Filebrowser testing update SVN r29088 |2013-10-22 23:45:14 +0000 --- src/org/labkey/test/tests/SpecimenExportTest.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/org/labkey/test/tests/SpecimenExportTest.java b/src/org/labkey/test/tests/SpecimenExportTest.java index 0e4f7f95..cdcc3456 100644 --- a/src/org/labkey/test/tests/SpecimenExportTest.java +++ b/src/org/labkey/test/tests/SpecimenExportTest.java @@ -144,8 +144,8 @@ private void importFromZipExport() goToFolderManagement(); clickAndWait(Locator.linkWithText("Import")); clickButtonContainingText("Import Folder Using Pipeline"); - _extHelper.expandFileBrowserRootNode(); - _extHelper.selectFileBrowserItem("export/"); + _fileBrowserHelper.expandFileBrowserRootNode(); + _fileBrowserHelper.selectFileBrowserItem("export/"); click(Locator.xpath("//div[contains(@class, 'x-grid3-cell-inner') and starts-with(text(), 'My Study_')]")); selectImportDataAction("Import Folder"); waitForPipelineJobsToComplete(2, "Folder import", false); @@ -154,7 +154,7 @@ private void importFromZipExport() private void verifySpecimenSettingsInArchive() { log("verify specimen settings in study.xml"); - _extHelper.selectFileBrowserItem("export/study/study.xml"); + _fileBrowserHelper.selectFileBrowserItem("export/study/study.xml"); doubleClick(Locator.xpath("//div[contains(@class, 'x-grid3-cell-inner') and text()='study.xml']")); waitForText(""); @@ -162,7 +162,7 @@ private void verifySpecimenSettingsInArchive() clickAndWait(Locator.linkWithText("Manage Files")); click(Locator.css("button.iconFolderTree")); shortWait().until(ExpectedConditions.visibilityOf(Locator.xpath("id('fileBrowser')//div[contains(@id, 'xsplit')]").findElement(getDriver()))); - _extHelper.selectFileBrowserItem("export/study/specimens/specimen_settings.xml"); + _fileBrowserHelper.selectFileBrowserItem("export/study/specimens/specimen_settings.xml"); doubleClick(Locator.xpath("//div[contains(@class, 'x-grid3-cell-inner') and text()='specimen_settings.xml']")); waitForText(" Date: Wed, 23 Oct 2013 19:25:31 +0000 Subject: [PATCH 129/494] Temp revert on filebrowser to get drt running SVN r29107 |2013-10-23 19:25:31 +0000 --- src/org/labkey/test/tests/SpecimenExportTest.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/org/labkey/test/tests/SpecimenExportTest.java b/src/org/labkey/test/tests/SpecimenExportTest.java index cdcc3456..0e4f7f95 100644 --- a/src/org/labkey/test/tests/SpecimenExportTest.java +++ b/src/org/labkey/test/tests/SpecimenExportTest.java @@ -144,8 +144,8 @@ private void importFromZipExport() goToFolderManagement(); clickAndWait(Locator.linkWithText("Import")); clickButtonContainingText("Import Folder Using Pipeline"); - _fileBrowserHelper.expandFileBrowserRootNode(); - _fileBrowserHelper.selectFileBrowserItem("export/"); + _extHelper.expandFileBrowserRootNode(); + _extHelper.selectFileBrowserItem("export/"); click(Locator.xpath("//div[contains(@class, 'x-grid3-cell-inner') and starts-with(text(), 'My Study_')]")); selectImportDataAction("Import Folder"); waitForPipelineJobsToComplete(2, "Folder import", false); @@ -154,7 +154,7 @@ private void importFromZipExport() private void verifySpecimenSettingsInArchive() { log("verify specimen settings in study.xml"); - _fileBrowserHelper.selectFileBrowserItem("export/study/study.xml"); + _extHelper.selectFileBrowserItem("export/study/study.xml"); doubleClick(Locator.xpath("//div[contains(@class, 'x-grid3-cell-inner') and text()='study.xml']")); waitForText(""); @@ -162,7 +162,7 @@ private void verifySpecimenSettingsInArchive() clickAndWait(Locator.linkWithText("Manage Files")); click(Locator.css("button.iconFolderTree")); shortWait().until(ExpectedConditions.visibilityOf(Locator.xpath("id('fileBrowser')//div[contains(@id, 'xsplit')]").findElement(getDriver()))); - _fileBrowserHelper.selectFileBrowserItem("export/study/specimens/specimen_settings.xml"); + _extHelper.selectFileBrowserItem("export/study/specimens/specimen_settings.xml"); doubleClick(Locator.xpath("//div[contains(@class, 'x-grid3-cell-inner') and text()='specimen_settings.xml']")); waitForText(" Date: Sat, 26 Oct 2013 00:12:36 +0000 Subject: [PATCH 130/494] Filebrowser test fixes/bug fixes SVN r29176 |2013-10-26 00:12:36 +0000 --- src/org/labkey/test/tests/SpecimenExportTest.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/org/labkey/test/tests/SpecimenExportTest.java b/src/org/labkey/test/tests/SpecimenExportTest.java index 0e4f7f95..cdcc3456 100644 --- a/src/org/labkey/test/tests/SpecimenExportTest.java +++ b/src/org/labkey/test/tests/SpecimenExportTest.java @@ -144,8 +144,8 @@ private void importFromZipExport() goToFolderManagement(); clickAndWait(Locator.linkWithText("Import")); clickButtonContainingText("Import Folder Using Pipeline"); - _extHelper.expandFileBrowserRootNode(); - _extHelper.selectFileBrowserItem("export/"); + _fileBrowserHelper.expandFileBrowserRootNode(); + _fileBrowserHelper.selectFileBrowserItem("export/"); click(Locator.xpath("//div[contains(@class, 'x-grid3-cell-inner') and starts-with(text(), 'My Study_')]")); selectImportDataAction("Import Folder"); waitForPipelineJobsToComplete(2, "Folder import", false); @@ -154,7 +154,7 @@ private void importFromZipExport() private void verifySpecimenSettingsInArchive() { log("verify specimen settings in study.xml"); - _extHelper.selectFileBrowserItem("export/study/study.xml"); + _fileBrowserHelper.selectFileBrowserItem("export/study/study.xml"); doubleClick(Locator.xpath("//div[contains(@class, 'x-grid3-cell-inner') and text()='study.xml']")); waitForText(""); @@ -162,7 +162,7 @@ private void verifySpecimenSettingsInArchive() clickAndWait(Locator.linkWithText("Manage Files")); click(Locator.css("button.iconFolderTree")); shortWait().until(ExpectedConditions.visibilityOf(Locator.xpath("id('fileBrowser')//div[contains(@id, 'xsplit')]").findElement(getDriver()))); - _extHelper.selectFileBrowserItem("export/study/specimens/specimen_settings.xml"); + _fileBrowserHelper.selectFileBrowserItem("export/study/specimens/specimen_settings.xml"); doubleClick(Locator.xpath("//div[contains(@class, 'x-grid3-cell-inner') and text()='specimen_settings.xml']")); waitForText(" Date: Sat, 26 Oct 2013 20:20:03 +0000 Subject: [PATCH 131/494] Remove usages of file browser helpers from ExtHelper, ExtHelperWD, BaseSeleniumWebTest, and BaseWebDriverTest More usage of FileBrowserHelper.imporFile SVN r29178 |2013-10-26 20:20:03 +0000 --- src/org/labkey/test/tests/SpecimenExportTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/org/labkey/test/tests/SpecimenExportTest.java b/src/org/labkey/test/tests/SpecimenExportTest.java index cdcc3456..50180f46 100644 --- a/src/org/labkey/test/tests/SpecimenExportTest.java +++ b/src/org/labkey/test/tests/SpecimenExportTest.java @@ -147,7 +147,7 @@ private void importFromZipExport() _fileBrowserHelper.expandFileBrowserRootNode(); _fileBrowserHelper.selectFileBrowserItem("export/"); click(Locator.xpath("//div[contains(@class, 'x-grid3-cell-inner') and starts-with(text(), 'My Study_')]")); - selectImportDataAction("Import Folder"); + _fileBrowserHelper.selectImportDataAction("Import Folder"); waitForPipelineJobsToComplete(2, "Folder import", false); } From bc104b08c0640463c851fef65938c80f9759d7c5 Mon Sep 17 00:00:00 2001 From: Trey Chadick Date: Mon, 28 Oct 2013 17:12:47 +0000 Subject: [PATCH 132/494] Use Ext4 compatible xpaths SVN r29190 |2013-10-28 17:12:47 +0000 --- src/org/labkey/test/tests/SpecimenExportTest.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/org/labkey/test/tests/SpecimenExportTest.java b/src/org/labkey/test/tests/SpecimenExportTest.java index 50180f46..501b6e9d 100644 --- a/src/org/labkey/test/tests/SpecimenExportTest.java +++ b/src/org/labkey/test/tests/SpecimenExportTest.java @@ -146,7 +146,7 @@ private void importFromZipExport() clickButtonContainingText("Import Folder Using Pipeline"); _fileBrowserHelper.expandFileBrowserRootNode(); _fileBrowserHelper.selectFileBrowserItem("export/"); - click(Locator.xpath("//div[contains(@class, 'x-grid3-cell-inner') and starts-with(text(), 'My Study_')]")); + click(Locator.tag("div").withClass("x4-grid-cell-inner").withPredicate("starts-with(text(), 'My Study_')")); _fileBrowserHelper.selectImportDataAction("Import Folder"); waitForPipelineJobsToComplete(2, "Folder import", false); } @@ -155,15 +155,14 @@ private void verifySpecimenSettingsInArchive() { log("verify specimen settings in study.xml"); _fileBrowserHelper.selectFileBrowserItem("export/study/study.xml"); - doubleClick(Locator.xpath("//div[contains(@class, 'x-grid3-cell-inner') and text()='study.xml']")); + doubleClick(Locator.xpath("//div[contains(@class, 'x4-grid-cell-inner') and text()='study.xml']")); waitForText(""); log("verify specimen_settings.xml"); clickAndWait(Locator.linkWithText("Manage Files")); click(Locator.css("button.iconFolderTree")); - shortWait().until(ExpectedConditions.visibilityOf(Locator.xpath("id('fileBrowser')//div[contains(@id, 'xsplit')]").findElement(getDriver()))); _fileBrowserHelper.selectFileBrowserItem("export/study/specimens/specimen_settings.xml"); - doubleClick(Locator.xpath("//div[contains(@class, 'x-grid3-cell-inner') and text()='specimen_settings.xml']")); + doubleClick(Locator.xpath("//div[contains(@class, 'x4-grid-cell-inner') and text()='specimen_settings.xml']")); waitForText("", From 49681e4d2ffeee99f11a2e9c1c9aeaf1e878f6d8 Mon Sep 17 00:00:00 2001 From: Nick Kerr Date: Mon, 28 Oct 2013 23:00:26 +0000 Subject: [PATCH 133/494] Revert for File Browser: 29186 29185 - except Browser.js 29184 29183 29178 - Except /webapp/File/panel/* 29176 - FilesWebPart.java and test module only SVN r29197 |2013-10-28 23:00:26 +0000 --- .../labkey/test/tests/SpecimenExportTest.java | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/org/labkey/test/tests/SpecimenExportTest.java b/src/org/labkey/test/tests/SpecimenExportTest.java index 501b6e9d..0e4f7f95 100644 --- a/src/org/labkey/test/tests/SpecimenExportTest.java +++ b/src/org/labkey/test/tests/SpecimenExportTest.java @@ -144,25 +144,26 @@ private void importFromZipExport() goToFolderManagement(); clickAndWait(Locator.linkWithText("Import")); clickButtonContainingText("Import Folder Using Pipeline"); - _fileBrowserHelper.expandFileBrowserRootNode(); - _fileBrowserHelper.selectFileBrowserItem("export/"); - click(Locator.tag("div").withClass("x4-grid-cell-inner").withPredicate("starts-with(text(), 'My Study_')")); - _fileBrowserHelper.selectImportDataAction("Import Folder"); + _extHelper.expandFileBrowserRootNode(); + _extHelper.selectFileBrowserItem("export/"); + click(Locator.xpath("//div[contains(@class, 'x-grid3-cell-inner') and starts-with(text(), 'My Study_')]")); + selectImportDataAction("Import Folder"); waitForPipelineJobsToComplete(2, "Folder import", false); } private void verifySpecimenSettingsInArchive() { log("verify specimen settings in study.xml"); - _fileBrowserHelper.selectFileBrowserItem("export/study/study.xml"); - doubleClick(Locator.xpath("//div[contains(@class, 'x4-grid-cell-inner') and text()='study.xml']")); + _extHelper.selectFileBrowserItem("export/study/study.xml"); + doubleClick(Locator.xpath("//div[contains(@class, 'x-grid3-cell-inner') and text()='study.xml']")); waitForText(""); log("verify specimen_settings.xml"); clickAndWait(Locator.linkWithText("Manage Files")); click(Locator.css("button.iconFolderTree")); - _fileBrowserHelper.selectFileBrowserItem("export/study/specimens/specimen_settings.xml"); - doubleClick(Locator.xpath("//div[contains(@class, 'x4-grid-cell-inner') and text()='specimen_settings.xml']")); + shortWait().until(ExpectedConditions.visibilityOf(Locator.xpath("id('fileBrowser')//div[contains(@id, 'xsplit')]").findElement(getDriver()))); + _extHelper.selectFileBrowserItem("export/study/specimens/specimen_settings.xml"); + doubleClick(Locator.xpath("//div[contains(@class, 'x-grid3-cell-inner') and text()='specimen_settings.xml']")); waitForText("", From 20d95497dbe2c2277ef6eb3348a891e0cfb9a9cc Mon Sep 17 00:00:00 2001 From: Josh Eckels Date: Tue, 19 Nov 2013 01:16:01 +0000 Subject: [PATCH 134/494] Fix SpecimenTest - don't be so picky about the exact format of the HTML of the email Issue 19035: **customizeEmail page parameter list contains duplicate entry for specimenList parameter SVN r29525 |2013-11-19 01:16:01 +0000 --- src/org/labkey/test/tests/SpecimenTest.java | 28 ++++++--------------- 1 file changed, 8 insertions(+), 20 deletions(-) diff --git a/src/org/labkey/test/tests/SpecimenTest.java b/src/org/labkey/test/tests/SpecimenTest.java index 226c4677..f00f35f8 100644 --- a/src/org/labkey/test/tests/SpecimenTest.java +++ b/src/org/labkey/test/tests/SpecimenTest.java @@ -109,7 +109,7 @@ protected void doCreateSteps() @Override @LogMethod(category = LogMethod.MethodType.VERIFICATION) - protected void doVerifySteps() + protected void doVerifySteps() throws IOException, HttpException { verifyActorDetails(); createRequest(); @@ -452,9 +452,6 @@ private void verifyAdditionalRequestFields() private final static String ATTACHMENT1 = "KCMC_Moshi_Ta_to_Aurum_Health_.tsv"; private final static String ATTACHMENT2 = "KCMC_Moshi_Ta_to_Aurum_Health_KO_%s.xls"; // Params: date(yyyy-MM-dd) private final String NOTIFICATION_TEMPLATE = // Params: Study Name, requestId, Study Name, requestId, Username, Date(yyyy-MM-dd) - "%s: Specimen Request Notification\n" + - "\n" + - "\n" + "Specimen request #%s was updated in %s.\n" + "\n" + "Request Details\n" + @@ -491,10 +488,10 @@ private void verifyAdditionalRequestFields() "10 999320520 KAA07YY0-01 Vst 501.0 1.0 ML Blood (Whole) Plasma, Unknown Processing EDTA N/A 2005-12-15 10:30:00.0 KCMC, Moshi, Tanzania Contract Lab Services, Johannesburg, South Africa LK 2006-01-15 00:00:00.0 LABK 39 15ml Cryovial true Contract Lab Services, Johannesburg, South Africa 350 true false This vial is unavailable because it is locked in a specimen request. false 2 2 2 0 0\n" + "11 999320520 KAA07YY0-02 Vst 501.0 1.0 ML Blood (Whole) Plasma, Unknown Processing EDTA N/A 2005-12-15 10:30:00.0 KCMC, Moshi, Tanzania Contract Lab Services, Johannesburg, South Africa LK 2006-01-15 00:00:00.0 LABK 39 15ml Cryovial true Contract Lab Services, Johannesburg, South Africa 350 true false This vial is unavailable because it is locked in a specimen request. false 2 2 2 0 0"; @LogMethod - private void verifyNotificationEmails() + private void verifyNotificationEmails() throws IOException, HttpException { String date = new SimpleDateFormat("yyyy-MM-dd").format(new Date()); - String notification = String.format(NOTIFICATION_TEMPLATE, getStudyLabel(), _requestId, getStudyLabel(), _requestId, PasswordUtil.getUsername(), date); + String notification = String.format(NOTIFICATION_TEMPLATE, _requestId, getStudyLabel(), _requestId, PasswordUtil.getUsername(), date); log("Check notification emails"); goToHome(); @@ -511,24 +508,15 @@ private void verifyNotificationEmails() Assert.assertTrue(!bodyText.contains(_specimen_McMichael)); Assert.assertTrue(bodyText.contains(_specimen_KCMC)); DataRegionTable mailTable = new DataRegionTable("EmailRecord", this, false, false); - Assert.assertEquals("Notification was not as expected", notification, mailTable.getDataAsText(emailIndex, "Message")); + String message = mailTable.getDataAsText(emailIndex, "Message"); + Assert.assertNotNull("No message found", message); + Assert.assertTrue("Notification was not as expected.\nExpected:\n" + notification + "\n\nActual:\n" + message, message.contains(notification)); String attachment1 = getAttribute(Locator.linkWithText(ATTACHMENT1), "href"); String attachment2 = getAttribute(Locator.linkWithText(String.format(ATTACHMENT2, date)), "href"); - try - { - Assert.assertEquals("Bad link to attachment: " + ATTACHMENT1, HttpStatus.SC_OK, WebTestHelper.getHttpGetResponse(attachment1)); - Assert.assertEquals("Bad link to attachment: " + String.format(ATTACHMENT2, date), HttpStatus.SC_OK, WebTestHelper.getHttpGetResponse(attachment2)); - } - catch (HttpException e) - { - Assert.fail("Failed to get HTTP client: "+e.getMessage()); - } - catch (IOException e) - { - Assert.fail("Failed to perform HTTP GET: "+e.getMessage()); - } + Assert.assertEquals("Bad link to attachment: " + ATTACHMENT1, HttpStatus.SC_OK, WebTestHelper.getHttpGetResponse(attachment1)); + Assert.assertEquals("Bad link to attachment: " + String.format(ATTACHMENT2, date), HttpStatus.SC_OK, WebTestHelper.getHttpGetResponse(attachment2)); clickAndWait(Locator.linkWithText("Request Link")); assertTextPresent("Specimen Request " + _requestId); From f8338e16f30d385219c4a36eb0b0710aa1942c0b Mon Sep 17 00:00:00 2001 From: Trey Chadick Date: Tue, 19 Nov 2013 21:12:37 +0000 Subject: [PATCH 135/494] static import junit asserts to clean up test code and ease testNG migration SVN r29543 |2013-11-19 21:12:37 +0000 --- src/org/labkey/test/tests/SpecimenTest.java | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/org/labkey/test/tests/SpecimenTest.java b/src/org/labkey/test/tests/SpecimenTest.java index f00f35f8..9b882444 100644 --- a/src/org/labkey/test/tests/SpecimenTest.java +++ b/src/org/labkey/test/tests/SpecimenTest.java @@ -18,7 +18,6 @@ import org.apache.http.HttpException; import org.apache.http.HttpStatus; -import org.junit.Assert; import org.junit.experimental.categories.Category; import org.labkey.test.Locator; import org.labkey.test.TestTimeoutException; @@ -38,6 +37,8 @@ import java.util.Arrays; import java.util.Date; +import static org.junit.Assert.*; + /** * User: brittp * Date: Mar 9, 2006 @@ -236,7 +237,7 @@ private void verifyActorDetails() int allCheckBoxes = getElementCount(Locator.xpath("//input[@type='checkbox' and @name='notificationIdPairs']")); int checkedCheckBoxes = getElementCount(Locator.xpath("//input[@type='checkbox' and @name='notificationIdPairs' and @checked]")); int disabledCheckBoxes = getElementCount(Locator.xpath("//input[@type='checkbox' and @name='notificationIdPairs' and @disabled]")); - Assert.assertTrue("Actor Notification: All actors should be notified if addresses configured.", allCheckBoxes == checkedCheckBoxes + disabledCheckBoxes); + assertTrue("Actor Notification: All actors should be notified if addresses configured.", allCheckBoxes == checkedCheckBoxes + disabledCheckBoxes); clickButton("Cancel"); } @@ -505,18 +506,18 @@ private void verifyNotificationEmails() throws IOException, HttpException click(Locator.linkContainingText("Specimen Request Notification").index(emailIndex)); shortWait().until(LabKeyExpectedConditions.emailIsExpanded(emailIndex + 1)); String bodyText = getText(Locator.id("dataregion_EmailRecord")); - Assert.assertTrue(!bodyText.contains(_specimen_McMichael)); - Assert.assertTrue(bodyText.contains(_specimen_KCMC)); + assertTrue(!bodyText.contains(_specimen_McMichael)); + assertTrue(bodyText.contains(_specimen_KCMC)); DataRegionTable mailTable = new DataRegionTable("EmailRecord", this, false, false); String message = mailTable.getDataAsText(emailIndex, "Message"); - Assert.assertNotNull("No message found", message); - Assert.assertTrue("Notification was not as expected.\nExpected:\n" + notification + "\n\nActual:\n" + message, message.contains(notification)); + assertNotNull("No message found", message); + assertTrue("Notification was not as expected.\nExpected:\n" + notification + "\n\nActual:\n" + message, message.contains(notification)); String attachment1 = getAttribute(Locator.linkWithText(ATTACHMENT1), "href"); String attachment2 = getAttribute(Locator.linkWithText(String.format(ATTACHMENT2, date)), "href"); - Assert.assertEquals("Bad link to attachment: " + ATTACHMENT1, HttpStatus.SC_OK, WebTestHelper.getHttpGetResponse(attachment1)); - Assert.assertEquals("Bad link to attachment: " + String.format(ATTACHMENT2, date), HttpStatus.SC_OK, WebTestHelper.getHttpGetResponse(attachment2)); + assertEquals("Bad link to attachment: " + ATTACHMENT1, HttpStatus.SC_OK, WebTestHelper.getHttpGetResponse(attachment1)); + assertEquals("Bad link to attachment: " + String.format(ATTACHMENT2, date), HttpStatus.SC_OK, WebTestHelper.getHttpGetResponse(attachment2)); clickAndWait(Locator.linkWithText("Request Link")); assertTextPresent("Specimen Request " + _requestId); @@ -884,7 +885,7 @@ private void exportSpecimenTest() for(String[] columnAndValue : columnAndValues) { log("Checking column: "+ columnAndValue[0]); - Assert.assertEquals(columnAndValue[1], auditTable.getDataAsText(0, columnAndValue[0])); + assertEquals(columnAndValue[1], auditTable.getDataAsText(0, columnAndValue[0])); } } } From b54eeb27548e8e7e97d1aea9886b19e018a2a728 Mon Sep 17 00:00:00 2001 From: Karl Lum Date: Fri, 22 Nov 2013 00:04:41 +0000 Subject: [PATCH 136/494] enable new filebrowser by default : merge changes from filebrowser branch through r29608 SVN r29609 |2013-11-22 00:04:41 +0000 --- .../labkey/test/tests/SpecimenExportTest.java | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/org/labkey/test/tests/SpecimenExportTest.java b/src/org/labkey/test/tests/SpecimenExportTest.java index 0e4f7f95..e29f561c 100644 --- a/src/org/labkey/test/tests/SpecimenExportTest.java +++ b/src/org/labkey/test/tests/SpecimenExportTest.java @@ -144,26 +144,25 @@ private void importFromZipExport() goToFolderManagement(); clickAndWait(Locator.linkWithText("Import")); clickButtonContainingText("Import Folder Using Pipeline"); - _extHelper.expandFileBrowserRootNode(); - _extHelper.selectFileBrowserItem("export/"); - click(Locator.xpath("//div[contains(@class, 'x-grid3-cell-inner') and starts-with(text(), 'My Study_')]")); - selectImportDataAction("Import Folder"); + _fileBrowserHelper.expandFileBrowserRootNode(); + _fileBrowserHelper.selectFileBrowserItem("export/"); + click(Locator.tag("div").withClass("x4-grid-cell-inner").withPredicate("starts-with(text(), 'My Study_')")); + _fileBrowserHelper.selectImportDataAction("Import Folder"); waitForPipelineJobsToComplete(2, "Folder import", false); } private void verifySpecimenSettingsInArchive() { log("verify specimen settings in study.xml"); - _extHelper.selectFileBrowserItem("export/study/study.xml"); - doubleClick(Locator.xpath("//div[contains(@class, 'x-grid3-cell-inner') and text()='study.xml']")); + _fileBrowserHelper.selectFileBrowserItem("export/study/study.xml"); + doubleClick(Locator.xpath("//div[contains(@class, 'x4-grid-cell-inner') and text()='study.xml']")); waitForText(""); log("verify specimen_settings.xml"); clickAndWait(Locator.linkWithText("Manage Files")); - click(Locator.css("button.iconFolderTree")); - shortWait().until(ExpectedConditions.visibilityOf(Locator.xpath("id('fileBrowser')//div[contains(@id, 'xsplit')]").findElement(getDriver()))); - _extHelper.selectFileBrowserItem("export/study/specimens/specimen_settings.xml"); - doubleClick(Locator.xpath("//div[contains(@class, 'x-grid3-cell-inner') and text()='specimen_settings.xml']")); + _fileBrowserHelper.selectFileBrowserItem("export/study/specimens/specimen_settings.xml"); + //TODO: broken: https://www.labkey.org/issues/home/Developer/issues/details.view?issueId=19029 + doubleClick(Locator.xpath("//div[contains(@class, 'x4-grid-cell-inner') and text()='specimen_settings.xml']")); waitForText("", From fe49413399dc52bb7b3f4f71ba2bf12387ecfa0a Mon Sep 17 00:00:00 2001 From: Trey Chadick Date: Fri, 22 Nov 2013 19:25:11 +0000 Subject: [PATCH 137/494] Wait for file list to load SVN r29626 |2013-11-22 19:25:11 +0000 --- src/org/labkey/test/tests/SpecimenExportTest.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/org/labkey/test/tests/SpecimenExportTest.java b/src/org/labkey/test/tests/SpecimenExportTest.java index e29f561c..48eb990b 100644 --- a/src/org/labkey/test/tests/SpecimenExportTest.java +++ b/src/org/labkey/test/tests/SpecimenExportTest.java @@ -144,9 +144,8 @@ private void importFromZipExport() goToFolderManagement(); clickAndWait(Locator.linkWithText("Import")); clickButtonContainingText("Import Folder Using Pipeline"); - _fileBrowserHelper.expandFileBrowserRootNode(); - _fileBrowserHelper.selectFileBrowserItem("export/"); - click(Locator.tag("div").withClass("x4-grid-cell-inner").withPredicate("starts-with(text(), 'My Study_')")); + _fileBrowserHelper.selectFileBrowserItem("/export/"); + waitAndClick(Locator.tag("div").withClass("x4-grid-cell-inner").withPredicate("starts-with(text(), 'My Study_')")); _fileBrowserHelper.selectImportDataAction("Import Folder"); waitForPipelineJobsToComplete(2, "Folder import", false); } From f1ede13a721b2e82d4473161a0ead5dea8a4ded7 Mon Sep 17 00:00:00 2001 From: Trey Chadick Date: Wed, 27 Nov 2013 17:21:45 +0000 Subject: [PATCH 138/494] Fix Locator for exported study SVN r29693 |2013-11-27 17:21:45 +0000 --- src/org/labkey/test/tests/SpecimenExportTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/org/labkey/test/tests/SpecimenExportTest.java b/src/org/labkey/test/tests/SpecimenExportTest.java index 48eb990b..6d7363b3 100644 --- a/src/org/labkey/test/tests/SpecimenExportTest.java +++ b/src/org/labkey/test/tests/SpecimenExportTest.java @@ -145,7 +145,7 @@ private void importFromZipExport() clickAndWait(Locator.linkWithText("Import")); clickButtonContainingText("Import Folder Using Pipeline"); _fileBrowserHelper.selectFileBrowserItem("/export/"); - waitAndClick(Locator.tag("div").withClass("x4-grid-cell-inner").withPredicate("starts-with(text(), 'My Study_')")); + waitAndClick(Locator.tag("tr").withClass("x4-grid-data-row").withAttributeContaining("data-recordid", "My Study_")); _fileBrowserHelper.selectImportDataAction("Import Folder"); waitForPipelineJobsToComplete(2, "Folder import", false); } From 5e542b4081f801d71a22bde14dc70f3378103aac Mon Sep 17 00:00:00 2001 From: Ryan Standley Date: Wed, 27 Nov 2013 23:33:13 +0000 Subject: [PATCH 139/494] ETL error tests for simple transform, and supporting changes SVN r29704 |2013-11-27 23:33:13 +0000 --- .../tests/CustomizeEmailTemplateTest.java | 163 ++++++++++++++++++ 1 file changed, 163 insertions(+) create mode 100644 src/org/labkey/test/tests/CustomizeEmailTemplateTest.java diff --git a/src/org/labkey/test/tests/CustomizeEmailTemplateTest.java b/src/org/labkey/test/tests/CustomizeEmailTemplateTest.java new file mode 100644 index 00000000..78b9d210 --- /dev/null +++ b/src/org/labkey/test/tests/CustomizeEmailTemplateTest.java @@ -0,0 +1,163 @@ +package org.labkey.test.tests; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.jetbrains.annotations.Nullable; +import org.junit.Assert; +import org.labkey.test.BaseWebDriverTest; +import org.junit.experimental.categories.Category; +import org.labkey.test.Locator; +import org.labkey.test.categories.DailyA; +import org.labkey.test.util.LogMethod; +import org.labkey.test.util.PasswordUtil; +import org.labkey.test.util.PortalHelper; +import static org.junit.Assert.*; + +/** + * User: RyanS + * Date: 11/19/13 + */ + +@Category(DailyA.class) +public class CustomizeEmailTemplateTest extends SpecimenBaseTest +{ + private static final String _projectName = "EmailTemplateProject"; + private final PortalHelper _portalHelper = new PortalHelper(this); + private static final String _assayPlan = "assay plan"; + private static final String _shipping = "123 main street"; + private static final String _comments = "this is my comment"; + private static final String _delim = "::"; + private static final String _emailBody1 = "
" + + "action==^action^" + _delim + "\n" + + "attachments==^attachments^" + _delim + "\n" + + "comments==^comments^" + _delim + "\n" + + "contextPath==^contextPath^" + _delim + "\n" + + "currentDateTime==^currentDateTime^" + _delim + "\n" + + "destinationLocation==^destinationLocation^" + _delim + "\n" + + "folderName==^folderName^" + _delim + "\n" + + "folderPath==^folderPath^" + _delim + "\n" + + "folderURL==^folderURL^" + _delim + "\n" + + "homePageURL==^homePageURL^" + _delim + "\n" + + "modifiedBy==^modifiedBy^" + _delim + "\n" + + "organizationName==^organizationName^" + _delim + "\n" + + "simpleStatus==^simpleStatus^" + _delim + "\n" + + "siteShortName==^siteShortName^" + _delim + "\n" + + "specimenList==^specimenList^" + _delim + "\n" + + "specimenRequestNumber==^specimenRequestNumber^" + _delim + "\n" + + "status==^status^" + _delim + "\n" + + "studyName==^studyName^" + _delim + "\n" + + "subjectSuffix==^subjectSuffix^" + _delim + "\n" + + "supportLink==^supportLink^" + _delim + "\n" + + "systemDescription==^systemDescription^" + _delim + "\n" + + "systemEmail==^systemEmail^" + + "
"; + + @Nullable + @Override + protected String getProjectName() + { + return _projectName; + } + + @Override + protected BrowserType bestBrowser() + { + return BrowserType.CHROME; + } + + @Override + @LogMethod(category = LogMethod.MethodType.SETUP) + protected void doCreateSteps() + { + enableEmailRecorder(); + initializeFolder(); + + clickButton("Create Study"); + setFormElement(Locator.name("label"), getStudyLabel()); + click(Locator.radioButtonByNameAndValue("simpleRepository", "false")); + clickButton("Create Study"); + + setPipelineRoot(getPipelinePath()); + startSpecimenImport(1); + waitForSpecimenImport(); + setupRequestStatuses(); + setupActorsAndGroups(); + setupRequestForm(); + setupActorNotification(); + setCustomNotificationTemplate(); + createSpecimenRequest(); + } + + private void setCustomNotificationTemplate() + { + clickTab("Manage"); + clickAndWait(Locator.linkWithText("Manage Notifications")); + checkCheckbox(Locator.checkboxById("newRequestNotifyCheckbox")); + waitForElement(Locator.xpath("//textarea[@id='newRequestNotify']")); + setFormElement(Locator.id("newRequestNotify"), "ryans@labkey.com"); + clickButton("Save"); + clickAndWait(Locator.linkWithText("Manage Notifications")); + clickAndWait(Locator.linkWithText("Edit Email Template")); + selectOptionByText(Locator.name("templateClass"), "Specimen request notification"); + setFormElement(Locator.id("emailSubject"), _projectName); + setFormElement(Locator.id("emailMessage"), _emailBody1); + clickButton("Save"); + } + + private void createSpecimenRequest() + { + clickTab("Specimen Data"); + click(Locator.linkWithText("By Individual Vial")); + click(Locator.xpath("//img[@src=\"/labkey/_images/cart.png\"]/../../../../..//td[@class=\"labkey-selectors\"]/input")); + clickMenuButton("Request Options", "Create New Request"); + selectOptionByText(Locator.name("destinationLocation"), DESTINATION_SITE); + setFormElement(Locator.id("input0"), _assayPlan); + setFormElement(Locator.id("input2"), _comments); + setFormElement(Locator.id("input1"), _shipping); + setFormElement(Locator.id("input3"), "sample last one input"); + clickButton("Create and View Details"); + clickButton("Submit Request", 0); + assertAlert("Once a request is submitted, its specimen list may no longer be modified. Continue?"); + } + + @Override + protected void doVerifySteps() throws Exception + { + goToModule("Dumbster"); + waitForElement(Locator.linkContainingText(_projectName)); + click(Locator.linkContainingText(_projectName)); + String emailBody= getTextFromElement(Locator.xpath("//Div[@id='body']")); + String[] bodyContents = emailBody.split(_delim); + Map emailNVPs = new HashMap<>(); + for (String line : bodyContents) + { + String[] nvp = line.split("=="); + if(nvp.length==2) + { + emailNVPs.put(nvp[0].trim(), nvp[1]); + } + if(nvp.length==1) + { + emailNVPs.put(nvp[0].trim(), ""); + } + } + Assert.assertEquals("New Request", emailNVPs.get("status")); + Assert.assertEquals("New Request Created", emailNVPs.get("action")); + Assert.assertEquals("/labkey", emailNVPs.get("contextPath")); + Assert.assertEquals("My Study", emailNVPs.get("folderName")); + Assert.assertEquals("/EmailTemplateProject/My Study", emailNVPs.get("folderPath")); + Assert.assertEquals("http://localhost:8080/labkey", emailNVPs.get("homePageURL")); + } + + + @Override + public String getAssociatedModuleDirectory() + { + return "server/modules/study"; + } + +} From 8c3a97777a1fba82165bcfeab67c5b1308e10091 Mon Sep 17 00:00:00 2001 From: Ryan Standley Date: Tue, 3 Dec 2013 17:52:45 +0000 Subject: [PATCH 140/494] fix for silly hardcoded base url SVN r29753 |2013-12-03 17:52:45 +0000 --- src/org/labkey/test/tests/CustomizeEmailTemplateTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/org/labkey/test/tests/CustomizeEmailTemplateTest.java b/src/org/labkey/test/tests/CustomizeEmailTemplateTest.java index 78b9d210..c91cf568 100644 --- a/src/org/labkey/test/tests/CustomizeEmailTemplateTest.java +++ b/src/org/labkey/test/tests/CustomizeEmailTemplateTest.java @@ -150,7 +150,7 @@ protected void doVerifySteps() throws Exception Assert.assertEquals("/labkey", emailNVPs.get("contextPath")); Assert.assertEquals("My Study", emailNVPs.get("folderName")); Assert.assertEquals("/EmailTemplateProject/My Study", emailNVPs.get("folderPath")); - Assert.assertEquals("http://localhost:8080/labkey", emailNVPs.get("homePageURL")); + Assert.assertEquals(getBaseURL(), emailNVPs.get("homePageURL")); } From 2d6678b867c6b6abde64107ff7c920317b24a1e8 Mon Sep 17 00:00:00 2001 From: Trey Chadick Date: Thu, 5 Dec 2013 18:47:01 +0000 Subject: [PATCH 141/494] Update test to verify files directly rather than through the fileBrowser SVN r29796 |2013-12-05 18:47:01 +0000 --- .../labkey/test/tests/SpecimenExportTest.java | 128 +++++++++++------- 1 file changed, 82 insertions(+), 46 deletions(-) diff --git a/src/org/labkey/test/tests/SpecimenExportTest.java b/src/org/labkey/test/tests/SpecimenExportTest.java index 6d7363b3..42fafbe3 100644 --- a/src/org/labkey/test/tests/SpecimenExportTest.java +++ b/src/org/labkey/test/tests/SpecimenExportTest.java @@ -21,7 +21,10 @@ import org.labkey.test.categories.DailyB; import org.labkey.test.categories.Specimen; import org.labkey.test.util.LogMethod; -import org.openqa.selenium.support.ui.ExpectedConditions; + +import static org.junit.Assert.*; + +import java.io.File; /** * User: cnathe @@ -153,52 +156,85 @@ private void importFromZipExport() private void verifySpecimenSettingsInArchive() { log("verify specimen settings in study.xml"); - _fileBrowserHelper.selectFileBrowserItem("export/study/study.xml"); - doubleClick(Locator.xpath("//div[contains(@class, 'x4-grid-cell-inner') and text()='study.xml']")); - waitForText(""); + File studyXml = new File(getDefaultFileRoot(getProjectName() + "/" + getFolderName()), "export/study/study.xml"); + String studyXmlText = getFileContents(studyXml).replaceAll(" *\r*\n *", "\n").replaceAll(" +", " "); + assertTrue(studyXmlText.contains("")); log("verify specimen_settings.xml"); - clickAndWait(Locator.linkWithText("Manage Files")); - _fileBrowserHelper.selectFileBrowserItem("export/study/specimens/specimen_settings.xml"); - //TODO: broken: https://www.labkey.org/issues/home/Developer/issues/details.view?issueId=19029 - doubleClick(Locator.xpath("//div[contains(@class, 'x4-grid-cell-inner') and text()='specimen_settings.xml']")); - waitForText("", - "Processing Location", - "Tube Type", - ""); - assertTextPresentInThisOrder( - "", - "", - "", - "", - "", - ""); - assertTextPresentInThisOrder( - "", - "", - "", - "", - "", - ""); - assertTextPresentInThisOrder( - "", - "", - "", - "", - "", - "", - "", - ""); - assertTextPresentInThisOrder( - "", - "Originating IRB Approval", - "Providing IRB Approval", - "Receiving IRB Approval", - "", - "SLG Approval", - "\n"+ + "\n"+ + "Processing Location\n"+ + "Processing Location\n"+ + "Processing Location\n"+ + "\n"+ + "\n"+ + "Tube Type\n"+ + "Tube Type\n"+ + "Tube Type\n"+ + "\n"+ + "")); + assertTrue(specimenSettingsXmlText.contains( + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "")); + assertTrue(specimenSettingsXmlText.contains( + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "")); + assertTrue(specimenSettingsXmlText.contains( + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "")); + assertTrue(specimenSettingsXmlText.contains( + "\n" + + "\n" + + "\n" + + "Originating IRB Approval\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "Providing IRB Approval\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "Receiving IRB Approval\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "SLG Approval\n" + + "\n" + + "\n" + + "")); } } From a93e87bacae82df584bb38c17192f2e464e64d5c Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Fri, 6 Dec 2013 21:37:11 +0000 Subject: [PATCH 142/494] Update licenses SVN r29829 |2013-12-06 21:37:11 +0000 --- .../test/tests/CustomizeEmailTemplateTest.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/org/labkey/test/tests/CustomizeEmailTemplateTest.java b/src/org/labkey/test/tests/CustomizeEmailTemplateTest.java index c91cf568..2dd9b841 100644 --- a/src/org/labkey/test/tests/CustomizeEmailTemplateTest.java +++ b/src/org/labkey/test/tests/CustomizeEmailTemplateTest.java @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2013 LabKey Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package org.labkey.test.tests; import java.util.ArrayList; From 0e9035eecec0fb3b047551859aceda05b2320a50 Mon Sep 17 00:00:00 2001 From: Trey Chadick Date: Tue, 10 Dec 2013 17:16:40 +0000 Subject: [PATCH 143/494] Wait for specimens webpart to render SVN r29871 |2013-12-10 17:16:40 +0000 --- src/org/labkey/test/tests/CustomizeEmailTemplateTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/org/labkey/test/tests/CustomizeEmailTemplateTest.java b/src/org/labkey/test/tests/CustomizeEmailTemplateTest.java index 2dd9b841..71f47139 100644 --- a/src/org/labkey/test/tests/CustomizeEmailTemplateTest.java +++ b/src/org/labkey/test/tests/CustomizeEmailTemplateTest.java @@ -126,7 +126,7 @@ private void setCustomNotificationTemplate() private void createSpecimenRequest() { clickTab("Specimen Data"); - click(Locator.linkWithText("By Individual Vial")); + waitAndClick(Locator.linkWithText("By Individual Vial")); click(Locator.xpath("//img[@src=\"/labkey/_images/cart.png\"]/../../../../..//td[@class=\"labkey-selectors\"]/input")); clickMenuButton("Request Options", "Create New Request"); selectOptionByText(Locator.name("destinationLocation"), DESTINATION_SITE); From 644f80fcbb4a49d5407ca8a5721a6d43cad35373 Mon Sep 17 00:00:00 2001 From: Trey Chadick Date: Tue, 10 Dec 2013 17:17:21 +0000 Subject: [PATCH 144/494] And wait for page load SVN r29872 |2013-12-10 17:17:21 +0000 --- src/org/labkey/test/tests/CustomizeEmailTemplateTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/org/labkey/test/tests/CustomizeEmailTemplateTest.java b/src/org/labkey/test/tests/CustomizeEmailTemplateTest.java index 71f47139..258bc69b 100644 --- a/src/org/labkey/test/tests/CustomizeEmailTemplateTest.java +++ b/src/org/labkey/test/tests/CustomizeEmailTemplateTest.java @@ -126,7 +126,7 @@ private void setCustomNotificationTemplate() private void createSpecimenRequest() { clickTab("Specimen Data"); - waitAndClick(Locator.linkWithText("By Individual Vial")); + waitAndClickAndWait(Locator.linkWithText("By Individual Vial")); click(Locator.xpath("//img[@src=\"/labkey/_images/cart.png\"]/../../../../..//td[@class=\"labkey-selectors\"]/input")); clickMenuButton("Request Options", "Create New Request"); selectOptionByText(Locator.name("destinationLocation"), DESTINATION_SITE); From c95e32262fb48aa2d0bfaa5c6171dfc58a086575 Mon Sep 17 00:00:00 2001 From: Dave Bradlee Date: Wed, 18 Dec 2013 04:12:32 +0000 Subject: [PATCH 145/494] Specimen Provisioning SVN r29981 |2013-12-18 04:12:32 +0000 --- src/org/labkey/test/tests/SpecimenImportTest.java | 5 ++++- src/org/labkey/test/tests/SpecimenTest.java | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/org/labkey/test/tests/SpecimenImportTest.java b/src/org/labkey/test/tests/SpecimenImportTest.java index 51ab1634..81d30d81 100644 --- a/src/org/labkey/test/tests/SpecimenImportTest.java +++ b/src/org/labkey/test/tests/SpecimenImportTest.java @@ -71,7 +71,10 @@ protected void doVerifySteps() protected void goToImport() { waitAndClickAndWait(Locator.linkContainingText("Data")); - waitAndClickAndWait(Locator.linkContainingText("Import Specimens")); + waitForText("No specimens found"); + waitForText("Import Specimens"); + sleep(100); + clickAndWait(Locator.linkContainingText("Import Specimens")); } protected void checkRequiredFields(boolean visit) diff --git a/src/org/labkey/test/tests/SpecimenTest.java b/src/org/labkey/test/tests/SpecimenTest.java index 9b882444..bea83c40 100644 --- a/src/org/labkey/test/tests/SpecimenTest.java +++ b/src/org/labkey/test/tests/SpecimenTest.java @@ -145,6 +145,7 @@ private void disableRequests() private void verifyRequestEnabled() { clickFolder(getFolderName()); + waitForElement(Locator.linkWithText("By Individual Vial")); assertTextPresent("Specimen Requests"); clickAndWait(Locator.linkWithText("By Individual Vial")); assertElementPresent(Locator.navButton("Request Options")); @@ -529,6 +530,7 @@ private void verifyRequestCancel() goToProjectHome(); clickFolder(getFolderName()); + waitForElement(Locator.linkWithText("By Individual Vial")); waitAndClick(Locator.xpath("//span[text() = 'Specimen Requests']/../../a")); waitAndClick(Locator.linkWithText("View Current Requests")); From 87f6e565fbf251b9be6dd4fc7c61093bc8f22c0d Mon Sep 17 00:00:00 2001 From: Trey Chadick Date: Tue, 21 Jan 2014 18:54:51 +0000 Subject: [PATCH 146/494] Use correct helper for + + <% + for (Cohort cohort : cohorts) + { + %> + + <% + } + %> + + + + <% + if (study.isAdvancedCohorts()) + { + %> + + Cohort filter type + + + + + <% + } + else + { + %> + + <% + } + } + + if (showParticipantGroups && factory.allowsParticipantGroupFilter()) + { + %> + + <%= h(study.getSubjectNounSingular()) %> Group + + + + + <% + } + if (factory.allowsAvailabilityFilter()) + { + %> + + Availability status + + + + + <% + } + if (factory.allowsCustomViewFilter()) + { + HtmlString viewPickerHtml = factory.getCustomViewPicker(views); + %> + + Base + + <%= viewPickerHtml %> + + + <% + } + + // AdditionalFormInputs values are html generated in classes that extend SpecimenVisitReportParameters + List> additionalFormInputs = factory.getAdditionalFormInputHtml(user); + for (Pair inputPair : additionalFormInputs) + { + %> + + <%= h(inputPair.getKey()) %> + <%= inputPair.getValue() %> + + <% + } + boolean atLeastOneChecked = factory.isViewVialCount() || + factory.isViewParticipantCount() || + factory.isViewVolume() || + factory.isViewPtidList(); + %> + +   + + +
+ +
+ +
+ <% + if (factory.allowsParticipantAggregates()) + { + %> + +
+ + <% + } + %> + + + <% if (!bean.isListView()) + { + %> + + + <%= button("Refresh").submit(true).onClick("document['" + formName + "']['excelExport'].value=false;") %> + <%= button("Print").submit(true).onClick("document['" + formName + "']['_print'].value=1; document['" + formName + "']['excelExport'].value=false;") %> + <%= bean.hasReports() ? button("Export to Excel").submit(true).onClick("document['" + formName + "']['excelExport'].value=true;") : + button("Export to Excel").submit(true).onClick("document['" + formName + "']['excelExport'].value=true;").enabled(false) %> + + + <% + } + %> + + +
+
+ + + + +<% + } +%> + +<% + if (bean.isListView()) + FrameFactoryClassic.endTitleFrame(out); + } + } +%> diff --git a/specimen/src/org/labkey/specimen/view/configRequestabilityRules.jsp b/specimen/src/org/labkey/specimen/view/configRequestabilityRules.jsp new file mode 100644 index 00000000..6cde96c9 --- /dev/null +++ b/specimen/src/org/labkey/specimen/view/configRequestabilityRules.jsp @@ -0,0 +1,672 @@ +<% +/* + * 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.study.StudyUrls"%> +<%@ page import="org.labkey.api.view.ActionURL"%> +<%@ page import="org.labkey.api.view.template.ClientDependencies" %> +<%@ page import="org.labkey.api.specimen.importer.RequestabilityManager" %> +<%@ page extends="org.labkey.api.jsp.JspBase" %> +<%@ taglib prefix="labkey" uri="http://www.labkey.org/taglib" %> +<%! + @Override + public void addClientDependencies(ClientDependencies dependencies) + { + dependencies.add("Ext3"); + } +%> +<% + // TODO: This should use Selector.js + Container c = getContainer(); +%> + + + + + + + + + + + + + + + +
Requestability Rule Configuration
+ Whether a given vial is requestable is determined by running a series of configurable rules. Each + rule may change the requestability state of any vial(s). Rules are run in order, so a vial's final state will be + determined by the last rule to affect that vial.

+ Note: if present, the <%= h(RequestabilityManager.RuleType.LOCKED_IN_REQUEST.getName()) %> + ensures that a single vial can never be part of two simultaneous requests. + +
 
+ +
+ + + + + + + +<% + for (RequestabilityManager.RuleType type : RequestabilityManager.RuleType.values()) + { +%> + + + + +<% + } +%> +
Available Rule Types
<%= h(type.getName())%><%= h(type.getDescription())%>
diff --git a/specimen/src/org/labkey/specimen/view/configurationRequired.jsp b/specimen/src/org/labkey/specimen/view/configurationRequired.jsp new file mode 100644 index 00000000..e95e0ba9 --- /dev/null +++ b/specimen/src/org/labkey/specimen/view/configurationRequired.jsp @@ -0,0 +1,20 @@ +<% +/* + * 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 extends="org.labkey.api.jsp.JspBase" %> +Specimen management is not configured for this study.

+A project administrator must first configure request statuses and actors before this action can be performed. diff --git a/specimen/src/org/labkey/specimen/view/customizeSpecimenReportWebPart.jsp b/specimen/src/org/labkey/specimen/view/customizeSpecimenReportWebPart.jsp new file mode 100644 index 00000000..1ad2b3da --- /dev/null +++ b/specimen/src/org/labkey/specimen/view/customizeSpecimenReportWebPart.jsp @@ -0,0 +1,71 @@ +<% +/* + * 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. + */ +%> +<%@ page import="org.labkey.api.specimen.actions.ReportConfigurationBean" %> +<%@ page import="org.labkey.api.specimen.report.SpecimenVisitReportParameters" %> +<%@ page import="org.labkey.api.view.HttpView" %> +<%@ page import="org.labkey.api.view.JspView" %> +<%@ page import="org.labkey.api.view.Portal.WebPart" %> +<%@ page import="org.labkey.specimen.view.SpecimenReportWebPartFactory" %> +<%@ taglib prefix="labkey" uri="http://www.labkey.org/taglib" %> +<%@ page extends="org.labkey.api.jsp.JspBase" %> +<% + JspView me = (JspView) HttpView.currentView(); + WebPart webpart = me.getModelBean(); + String currentReportName = webpart.getPropertyMap().get(SpecimenReportWebPartFactory.REPORT_TYPE_PARAMETER_NAME); + + ReportConfigurationBean bean = new ReportConfigurationBean(getViewContext()); +%> + + + + + + + + + + + +
+ Choose the specimen report to display in this webpart
+
+ Type + +
+ + + + +
+ <%= button("Submit").submit(true) %> + <%= button("Cancel").href(getContainer().getStartURL(getUser())) %> +
+
+
diff --git a/specimen/src/org/labkey/specimen/view/pivot.jsp b/specimen/src/org/labkey/specimen/view/pivot.jsp new file mode 100644 index 00000000..c305196c --- /dev/null +++ b/specimen/src/org/labkey/specimen/view/pivot.jsp @@ -0,0 +1,497 @@ +<% +/* + * Copyright (c) 2015-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. + */ +%> +<%@ taglib prefix="labkey" uri="http://www.labkey.org/taglib" %> +<%@ page import="org.labkey.api.view.template.ClientDependencies" %> +<%@ page extends="org.labkey.api.jsp.JspBase" %> +<%! + @Override + public void addClientDependencies(ClientDependencies dependencies) + { + dependencies.add("Ext4"); + dependencies.add("query/olap.js"); + } +%> +<% + String configId = "Study:/specimens"; + String schemaName = "Study"; + String cubeName = "SpecimenCube"; + int uid = getRequestScopedUID(); + String pivotDesignerId = "pivotDesigner" + uid; + String cellsetId = "cellset" + uid; +%> + + + + + + + + + + + + +
pages (NYI)
 
filters
 
columns
 
rows
measures
 
+ + + +
+
+

I'm a page header

+
+
diff --git a/specimen/src/org/labkey/specimen/view/relevantRequests.jsp b/specimen/src/org/labkey/specimen/view/relevantRequests.jsp new file mode 100644 index 00000000..51e1f021 --- /dev/null +++ b/specimen/src/org/labkey/specimen/view/relevantRequests.jsp @@ -0,0 +1,19 @@ +<% +/* + * 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 extends="org.labkey.api.jsp.JspBase" %> +There are no requests involving this specimen. diff --git a/specimen/src/org/labkey/specimen/view/search.jsp b/specimen/src/org/labkey/specimen/view/search.jsp new file mode 100644 index 00000000..5d0b6b0e --- /dev/null +++ b/specimen/src/org/labkey/specimen/view/search.jsp @@ -0,0 +1,92 @@ +<% +/* + * 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.view.HttpView" %> +<%@ page import="org.labkey.api.view.JspView" %> +<%@ page import="org.labkey.api.view.template.ClientDependencies" %> +<%@ page import="org.labkey.api.view.template.ClientDependency" %> +<%@ page import="org.labkey.api.specimen.SpecimenSearchBean" %> +<%@ page extends="org.labkey.api.jsp.JspBase" %> +<%! + @Override + public void addClientDependencies(ClientDependencies dependencies) + { + dependencies.add("clientapi"); + dependencies.add("Ext4ClientApi"); + dependencies.add("extWidgets/SearchPanel.js"); + dependencies.add("ux/CheckCombo/CheckCombo.js"); + dependencies.add("ux/CheckCombo/CheckCombo.css"); + dependencies.add("study/SpecimenSearchPanel.js"); + dependencies.add(ClientDependency.fromModuleName("Study")); + } +%> +<% + JspView me = (JspView) HttpView.currentView(); + int webPartId = me.getModelBean().getWebPartId(); + String renderTarget = "labkey-specimen-search-"+ webPartId; +%> +
+ From af6037ee4eecec2629cb1a32f7e025c3961480f3 Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Fri, 5 Feb 2021 14:00:31 -0800 Subject: [PATCH 326/494] Investigate intermittent failures with SpecimenTest Original PR: https://github.com/LabKey/testAutomation/pull/630 --- src/org/labkey/test/tests/SpecimenTest.java | 22 ++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/org/labkey/test/tests/SpecimenTest.java b/src/org/labkey/test/tests/SpecimenTest.java index adb81b0f..a0f1400f 100644 --- a/src/org/labkey/test/tests/SpecimenTest.java +++ b/src/org/labkey/test/tests/SpecimenTest.java @@ -194,7 +194,7 @@ private void disableRequests() clickButton("Submit"); } - private static String[] requestColumns = {"Locked In Request", "Requestable", "Available", "Availability Reason", "Locked In Request Count", "Available Count", "Expected Available Count"}; + private static final String[] requestColumns = {"Locked In Request", "Requestable", "Available", "Availability Reason", "Locked In Request Count", "Available Count", "Expected Available Count"}; private void verifyRequestEnabled() { @@ -662,6 +662,8 @@ private void verifySpecimenTableAttachments() manageNotificationsPage.setNotificationUsers(List.of(PasswordUtil.getUsername())); manageNotificationsPage.setSpecimenAttachmentType(SpecimensAttachment.EXCEL); manageNotificationsPage.clickSave(); + // Temporary check while tracking down intermittent failure with attachment type + verifySpecimenAttachmentOptionChecked(SpecimensAttachment.EXCEL); log("Create request with excel specimen attachment"); goToSpecimenData(); @@ -679,13 +681,13 @@ private void verifySpecimenTableAttachments() }); waitForElement(Locator.css("h3").withText("Your request has been successfully submitted.")); - log("Verify specimen tsv attachment"); + log("Verify specimen xls attachment"); EmailRecordTable emailRecordTable = goToEmailRecord(); emailRecordTable.clickSubject("Study 001: Specimen Request Notification"); waitForElement(Locator.linkWithText("SpecimenDetail.xls")); - // Each notification should be have only the specimen request details, no specimen list + // Each notification should have only the specimen request details, no specimen list assertElementPresent(Locator.css("#email_body_1 > table"), 1); enableEmailRecorder(); // Clear out previous notifications @@ -697,6 +699,8 @@ private void verifySpecimenTableAttachments() manageNotificationsPage = new ManageNotificationsPage(getDriver()); manageNotificationsPage.setSpecimenAttachmentType(SpecimensAttachment.TEXT); manageNotificationsPage.clickSave(); + // Temporary check while tracking down intermittent failure with attachment type + verifySpecimenAttachmentOptionChecked(SpecimensAttachment.TEXT); log("Create request with text specimen attachment"); goToSpecimenData(); @@ -714,16 +718,24 @@ private void verifySpecimenTableAttachments() }); waitForElement(Locator.css("h3").withText("Your request has been successfully submitted.")); - log("Verify specimen xls attachment"); + log("Verify specimen tsv attachment"); emailRecordTable = goToEmailRecord(); emailRecordTable.clickSubject("Study 001: Specimen Request Notification"); waitForElement(Locator.linkWithText("SpecimenDetail.tsv")); - // Each notification should be have only the specimen request details, no specimen list + // Each notification should have only the specimen request details, no specimen list assertElementPresent(Locator.css("#email_body_1 > table"), 1); } + private void verifySpecimenAttachmentOptionChecked(SpecimensAttachment option) + { + assertTitleContains("Manage Study: /" + getProjectName() + "/" + getFolderName()); + waitAndClickAndWait(Locator.linkWithText("Manage Notifications")); + ManageNotificationsPage manageNotificationsPage = new ManageNotificationsPage(getDriver()); + assertTrue(manageNotificationsPage.isSpecimenAttachmentTypeChecked(option)); + } + @LogMethod private void verifyInactiveUsersInRequests() { From 06c6c72bb402bddbbe4d4e5bbad5d4fe33ee0894 Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Wed, 10 Feb 2021 11:00:44 -0800 Subject: [PATCH 327/494] Restore creation of specimen custom columns during publish study Original PR: https://github.com/LabKey/platform/pull/1981 --- .../labkey/specimen/importer/SpecimenSchemaImporter.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/specimen/src/org/labkey/specimen/importer/SpecimenSchemaImporter.java b/specimen/src/org/labkey/specimen/importer/SpecimenSchemaImporter.java index 23390674..db8732e2 100644 --- a/specimen/src/org/labkey/specimen/importer/SpecimenSchemaImporter.java +++ b/specimen/src/org/labkey/specimen/importer/SpecimenSchemaImporter.java @@ -98,7 +98,11 @@ private boolean containsSchemasToImport(SimpleStudyImportContext ctx) throws Imp try { VirtualFile specimenDir = ctx.getRoot().getDir(specimens.getDir()); - if (null != specimenDir && null != specimens.getFile()) + // Second check fails for the publish study case, since we don't create a zipped archive inside of the + // MemoryVirtualFile. During specimen migration, it was convenient to move calling of this method to + // process(), but that meant the check started to be called (and failed) in the publish case... which + // meant no custom columns. + if (null != specimenDir) // && null != specimens.getFile()) { XmlObject schemaXml = specimenDir.getXmlBean(SpecimenArchiveDataTypes.SCHEMA_FILENAME); return (schemaXml instanceof TablesDocument); From 899f3ca6f14604929944bdcae6e1d0d6716ea2be Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Fri, 12 Feb 2021 08:59:24 -0800 Subject: [PATCH 328/494] Move pipeline jobs and tasks to specimen module Original PR: https://github.com/LabKey/platform/pull/1984 * Create specimenContext.xml and register most specimen pipelines and tasks there * Move FileAnalysisSpecimenTask, SpecimenReloadJob, SpecimenReloadJobSupport, SpecimenReloadTask, and StandaloneSpecimenTask to specimen-main --- .../org/labkey/specimen/SpecimenModule.java | 25 +++- .../labkey/specimen/SpecimenServiceImpl.java | 2 +- .../pipeline/FileAnalysisSpecimenTask.java | 137 ++++++++++++++++++ .../specimen/pipeline/SpecimenReloadJob.java | 78 ++++++++++ .../pipeline/SpecimenReloadJobSupport.java | 33 +++++ .../specimen/pipeline/SpecimenReloadTask.java | 108 ++++++++++++++ .../pipeline/StandaloneSpecimenTask.java | 50 +++++++ .../WEB-INF/specimen/specimenContext.xml | 81 +++++++++++ 8 files changed, 510 insertions(+), 4 deletions(-) create mode 100644 specimen/src/org/labkey/specimen/pipeline/FileAnalysisSpecimenTask.java create mode 100644 specimen/src/org/labkey/specimen/pipeline/SpecimenReloadJob.java create mode 100644 specimen/src/org/labkey/specimen/pipeline/SpecimenReloadJobSupport.java create mode 100644 specimen/src/org/labkey/specimen/pipeline/SpecimenReloadTask.java create mode 100644 specimen/src/org/labkey/specimen/pipeline/StandaloneSpecimenTask.java create mode 100644 specimen/webapp/WEB-INF/specimen/specimenContext.xml diff --git a/specimen/src/org/labkey/specimen/SpecimenModule.java b/specimen/src/org/labkey/specimen/SpecimenModule.java index 9dc555ed..cc20db71 100644 --- a/specimen/src/org/labkey/specimen/SpecimenModule.java +++ b/specimen/src/org/labkey/specimen/SpecimenModule.java @@ -17,13 +17,14 @@ package org.labkey.specimen; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.labkey.api.attachments.AttachmentService; import org.labkey.api.audit.AuditLogService; import org.labkey.api.data.Container; import org.labkey.api.data.ContainerManager; import org.labkey.api.exp.property.PropertyService; -import org.labkey.api.module.CodeOnlyModule; import org.labkey.api.module.ModuleContext; +import org.labkey.api.module.SpringModule; import org.labkey.api.security.roles.RoleManager; import org.labkey.api.specimen.SpecimenRequestManager; import org.labkey.api.specimen.SpecimensPage; @@ -62,7 +63,7 @@ import java.util.List; import java.util.Set; -public class SpecimenModule extends CodeOnlyModule +public class SpecimenModule extends SpringModule { public static final String NAME = "Specimen"; @@ -84,6 +85,18 @@ protected Collection createWebPartFactories() ); } + @Override + public @Nullable Double getSchemaVersion() + { + return null; + } + + @Override + public boolean hasScripts() + { + return false; + } + @Override protected void init() { @@ -108,7 +121,7 @@ protected void init() } @Override - public void doStartup(ModuleContext moduleContext) + protected void startupAfterSpringConfig(ModuleContext moduleContext) { ContainerManager.addContainerListener(SpecimenRequestManager.get()); @@ -145,6 +158,12 @@ public Set getUnitTests() ); } + @Override + public @NotNull Collection getSchemaNames() + { + return Set.of(); + } + @Override public @NotNull Set getIntegrationTests() { diff --git a/specimen/src/org/labkey/specimen/SpecimenServiceImpl.java b/specimen/src/org/labkey/specimen/SpecimenServiceImpl.java index 80ef631b..b7101292 100644 --- a/specimen/src/org/labkey/specimen/SpecimenServiceImpl.java +++ b/specimen/src/org/labkey/specimen/SpecimenServiceImpl.java @@ -43,7 +43,6 @@ import org.labkey.api.specimen.importer.SimpleSpecimenImporter; import org.labkey.api.specimen.importer.SpecimenColumn; import org.labkey.api.specimen.model.SpecimenTablesProvider; -import org.labkey.api.specimen.pipeline.SpecimenReloadJob; import org.labkey.api.study.ParticipantVisit; import org.labkey.api.study.SpecimenChangeListener; import org.labkey.api.study.SpecimenImportStrategyFactory; @@ -55,6 +54,7 @@ import org.labkey.api.util.Pair; import org.labkey.api.view.ActionURL; import org.labkey.api.view.ViewBackgroundInfo; +import org.labkey.specimen.pipeline.SpecimenReloadJob; import java.io.IOException; import java.util.ArrayList; diff --git a/specimen/src/org/labkey/specimen/pipeline/FileAnalysisSpecimenTask.java b/specimen/src/org/labkey/specimen/pipeline/FileAnalysisSpecimenTask.java new file mode 100644 index 00000000..9e12b126 --- /dev/null +++ b/specimen/src/org/labkey/specimen/pipeline/FileAnalysisSpecimenTask.java @@ -0,0 +1,137 @@ +package org.labkey.specimen.pipeline; + +import org.apache.commons.io.FileUtils; +import org.apache.commons.lang3.BooleanUtils; +import org.apache.tika.detect.DefaultDetector; +import org.apache.tika.io.TikaInputStream; +import org.apache.tika.metadata.Metadata; +import org.apache.tika.mime.MediaType; +import org.jetbrains.annotations.Nullable; +import org.labkey.api.admin.ImportException; +import org.labkey.api.admin.PipelineJobLoggerGetter; +import org.labkey.api.pipeline.PipelineJob; +import org.labkey.api.pipeline.PipelineJobException; +import org.labkey.api.pipeline.file.FileAnalysisJobSupport; +import org.labkey.api.specimen.pipeline.AbstractSpecimenTask; +import org.labkey.api.specimen.pipeline.AbstractSpecimenTaskFactory; +import org.labkey.api.study.importer.SimpleStudyImportContext; +import org.labkey.api.util.DateUtil; +import org.labkey.api.writer.FileSystemFile; +import org.labkey.api.writer.VirtualFile; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.Date; + +public class FileAnalysisSpecimenTask extends AbstractSpecimenTask +{ + public static final String MERGE_SPECIMEN = "mergeSpecimen"; + + public FileAnalysisSpecimenTask(FileAnalysisSpecimenTask.Factory factory, PipelineJob job) + { + super(factory, job); + } + + @Override + protected File getSpecimenFile(PipelineJob job) + { + FileAnalysisJobSupport support = job.getJobSupport(FileAnalysisJobSupport.class); + + // there should only be a single file associated with this task + assert support.getInputFiles().size() == 1; + return support.getInputFiles().get(0); + } + + @Override + protected SimpleStudyImportContext getImportContext(PipelineJob job) + { + return new SimpleStudyImportContext(job.getUser(), job.getContainer(), null, null, new PipelineJobLoggerGetter(job), null); + } + + @Override + protected boolean isMerge() + { + FileAnalysisJobSupport support = getJob().getJobSupport(FileAnalysisJobSupport.class); + boolean isMerge = false; + + if (support.getParameters().containsKey(MERGE_SPECIMEN)) + isMerge = BooleanUtils.toBoolean(support.getParameters().get(MERGE_SPECIMEN)); + + getJob().getLogger().info("Specimen merge option is set to : " + isMerge); + return isMerge; + } + + @Override + public AbstractSpecimenTask.ImportHelper getImportHelper() + { + return new ImportHelper(); + } + + private static class ImportHelper extends DefaultImportHelper + { + private File _tempDir; + + @Override + public VirtualFile getSpecimenDir(PipelineJob job, SimpleStudyImportContext ctx, @Nullable File inputFile) throws IOException, ImportException, PipelineJobException + { + if (inputFile != null) + { + try (TikaInputStream is = TikaInputStream.get(new FileInputStream(inputFile))) + { + // determine the type of file being imported + DefaultDetector detector = new DefaultDetector(); + MediaType type = detector.detect(is, new Metadata()); + + if (MediaType.APPLICATION_ZIP.equals(type)) + { + ctx.getLogger().info("Specimen archive detected"); + return super.getSpecimenDir(job, ctx, inputFile); + } + else if (MediaType.TEXT_PLAIN.equals(type)) + { + // this is a loose file that has already been extracted from the archive + // copy the single file to a temp directory and process it there + // + ctx.getLogger().info("Single specimen file detected, moving to a temp folder for processing."); + String tempDirName = DateUtil.formatDateTime(new Date(), "yyMMddHHmmssSSS"); + _tempDir = new File(inputFile.getParentFile(), tempDirName); + FileUtils.copyToDirectory(inputFile, _tempDir); + + return new FileSystemFile(_tempDir); + } + else + throw new RuntimeException("Unknown specimen file type uploaded"); + } + catch (IOException e) + { + throw new RuntimeException(e); + } + } + return null; + } + + @Override + public void afterImport(SimpleStudyImportContext ctx) + { + super.afterImport(ctx); + + if (_tempDir != null) + delete(_tempDir, ctx); + } + } + + public static class Factory extends AbstractSpecimenTaskFactory + { + public Factory() + { + super(FileAnalysisSpecimenTask.class); + } + + @Override + public PipelineJob.Task createTask(PipelineJob job) + { + return new FileAnalysisSpecimenTask(this, job); + } + } +} diff --git a/specimen/src/org/labkey/specimen/pipeline/SpecimenReloadJob.java b/specimen/src/org/labkey/specimen/pipeline/SpecimenReloadJob.java new file mode 100644 index 00000000..55d74a31 --- /dev/null +++ b/specimen/src/org/labkey/specimen/pipeline/SpecimenReloadJob.java @@ -0,0 +1,78 @@ +/* + * 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.specimen.pipeline; + +import org.labkey.api.pipeline.PipeRoot; +import org.labkey.api.pipeline.PipelineJobService; +import org.labkey.api.pipeline.TaskId; +import org.labkey.api.pipeline.TaskPipeline; +import org.labkey.api.specimen.pipeline.SpecimenBatch; +import org.labkey.api.study.SpecimenTransform; +import org.labkey.api.util.FileUtil; +import org.labkey.api.view.ViewBackgroundInfo; + +import java.io.File; +import java.io.Serializable; + +/** + * Created by klum on 5/24/2014. + */ +public class SpecimenReloadJob extends SpecimenBatch implements Serializable, SpecimenReloadJobSupport +{ + private String _transformName; + private SpecimenTransform.ExternalImportConfig _importConfig; + + // For serialization + protected SpecimenReloadJob() {} + + public SpecimenReloadJob(ViewBackgroundInfo info, PipeRoot root, String transformName) + { + super(info, null, root, false); + + File logFile = new File(root.getRootPath(), FileUtil.makeFileNameWithTimestamp("specimen_reload", "log")); + setLogFile(logFile); + _transformName = transformName; + } + + @Override + public void setSpecimenArchive(File archiveFile) + { + _definitionFile = archiveFile; + } + + @Override + public String getSpecimenTransform() + { + return _transformName; + } + + public void setExternalImportConfig(SpecimenTransform.ExternalImportConfig config) + { + _importConfig = config; + } + + @Override + public SpecimenTransform.ExternalImportConfig getExternalImportConfig() + { + return _importConfig; + } + + @Override + public TaskPipeline getTaskPipeline() + { + return PipelineJobService.get().getTaskPipeline(new TaskId(SpecimenReloadJob.class)); + } +} diff --git a/specimen/src/org/labkey/specimen/pipeline/SpecimenReloadJobSupport.java b/specimen/src/org/labkey/specimen/pipeline/SpecimenReloadJobSupport.java new file mode 100644 index 00000000..64c912fa --- /dev/null +++ b/specimen/src/org/labkey/specimen/pipeline/SpecimenReloadJobSupport.java @@ -0,0 +1,33 @@ +/* + * 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.specimen.pipeline; + +import org.labkey.api.specimen.pipeline.SpecimenJobSupport; +import org.labkey.api.study.SpecimenTransform; + +import java.io.File; + +/** + * Created by klum on 5/24/2014. + */ +public interface SpecimenReloadJobSupport extends SpecimenJobSupport +{ + void setSpecimenArchive(File archiveFile); + + String getSpecimenTransform(); + + SpecimenTransform.ExternalImportConfig getExternalImportConfig(); +} diff --git a/specimen/src/org/labkey/specimen/pipeline/SpecimenReloadTask.java b/specimen/src/org/labkey/specimen/pipeline/SpecimenReloadTask.java new file mode 100644 index 00000000..f1567ffc --- /dev/null +++ b/specimen/src/org/labkey/specimen/pipeline/SpecimenReloadTask.java @@ -0,0 +1,108 @@ +/* + * 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.specimen.pipeline; + +import org.jetbrains.annotations.NotNull; +import org.labkey.api.pipeline.AbstractTaskFactory; +import org.labkey.api.pipeline.AbstractTaskFactorySettings; +import org.labkey.api.pipeline.PipeRoot; +import org.labkey.api.pipeline.PipelineJob; +import org.labkey.api.pipeline.PipelineJobException; +import org.labkey.api.pipeline.PipelineService; +import org.labkey.api.pipeline.RecordedActionSet; +import org.labkey.api.study.SpecimenService; +import org.labkey.api.study.SpecimenTransform; +import org.labkey.api.util.FileType; +import org.labkey.api.util.FileUtil; + +import java.io.File; +import java.util.Collections; +import java.util.List; + +/** + * Created by klum on 5/24/2014. + */ +public class SpecimenReloadTask extends PipelineJob.Task +{ + private SpecimenReloadTask(Factory factory, PipelineJob job) + { + super(factory, job); + } + + @NotNull + @Override + public RecordedActionSet run() throws PipelineJobException + { + PipelineJob job = getJob(); + SpecimenReloadJobSupport support = job.getJobSupport(SpecimenReloadJobSupport.class); + String specimenTransformName = support.getSpecimenTransform(); + SpecimenTransform transform = SpecimenService.get().getSpecimenTransform(specimenTransformName); + + if (transform != null) + { + PipeRoot root = PipelineService.get().findPipelineRoot(job.getContainer()); + if (root != null) + { + File archive = new File(root.getRootPath(), FileUtil.makeFileNameWithTimestamp("specimen_reload", transform.getFileType().getDefaultSuffix())); + + transform.importFromExternalSource(job, support.getExternalImportConfig(), archive); + + support.setSpecimenArchive(archive); + return new RecordedActionSet(); + } + } + + throw new PipelineJobException("Failed to locate a specimen transform implementation for the name: " + specimenTransformName); + } + + public static class Factory extends AbstractTaskFactory + { + public Factory() + { + super(SpecimenReloadTask.class); + } + + @Override + public PipelineJob.Task createTask(PipelineJob job) + { + return new SpecimenReloadTask(this, job); + } + + @Override + public List getInputTypes() + { + return Collections.emptyList(); + } + + @Override + public List getProtocolActionNames() + { + return Collections.emptyList(); + } + + @Override + public String getStatusName() + { + return "RELOAD SPECIMEN"; + } + + @Override + public boolean isJobComplete(PipelineJob job) + { + return false; + } + } +} diff --git a/specimen/src/org/labkey/specimen/pipeline/StandaloneSpecimenTask.java b/specimen/src/org/labkey/specimen/pipeline/StandaloneSpecimenTask.java new file mode 100644 index 00000000..ec0fb435 --- /dev/null +++ b/specimen/src/org/labkey/specimen/pipeline/StandaloneSpecimenTask.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2009-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.specimen.pipeline; + +import org.labkey.api.pipeline.PipelineJob; +import org.labkey.api.specimen.pipeline.AbstractSpecimenTask; +import org.labkey.api.specimen.pipeline.AbstractSpecimenTaskFactory; + +/* +* User: adam +* Date: Sep 1, 2009 +* Time: 4:37:31 PM +*/ + +// This task is used to import specimen archives directly via the pipeline ui. SpecimenBatch is the associated pipeline job. +public class StandaloneSpecimenTask extends AbstractSpecimenTask +{ + private StandaloneSpecimenTask(Factory factory, PipelineJob job) + { + super(factory, job); + } + + public static class Factory extends AbstractSpecimenTaskFactory + { + public Factory() + { + super(StandaloneSpecimenTask.class); + } + + @Override + public PipelineJob.Task createTask(PipelineJob job) + { + return new StandaloneSpecimenTask(this, job); + } + } +} diff --git a/specimen/webapp/WEB-INF/specimen/specimenContext.xml b/specimen/webapp/WEB-INF/specimen/specimenContext.xml new file mode 100644 index 00000000..c6a80365 --- /dev/null +++ b/specimen/webapp/WEB-INF/specimen/specimenContext.xml @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + + + + + + org.labkey.specimen.pipeline.StandaloneSpecimenTask + + + + + + + + + + + + org.labkey.specimen.pipeline.SpecimenReloadTask + + + org.labkey.specimen.pipeline.StandaloneSpecimenTask + + + + + + + + + + + + + + + + + + + + + + + + org.labkey.specimen.pipeline.FileAnalysisSpecimenTask + + + + + + + + + + + + + + + + + + + From 62038f98b5fd36fd97d944e958662ac363ee9843 Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Tue, 16 Feb 2021 17:57:23 -0800 Subject: [PATCH 329/494] Run sans specimen module Original PR: https://github.com/LabKey/platform/pull/1994 --- specimen/module.properties | 8 ++++---- specimen/schemas/specimens.xsd | 7 +++++++ .../src/org/labkey/specimen/SpecimenModule.java | 17 +++-------------- .../labkey/specimen/SpecimenServiceImpl.java | 13 ------------- .../specimen/action/SpecimenApiController.java | 3 +-- .../test/tests/study/SpecimenMergeTest.java | 1 - 6 files changed, 15 insertions(+), 34 deletions(-) diff --git a/specimen/module.properties b/specimen/module.properties index 8c8db94f..33d1160a 100644 --- a/specimen/module.properties +++ b/specimen/module.properties @@ -1,7 +1,7 @@ ModuleClass: org.labkey.specimen.SpecimenModule -Label: FIXME! Capitalized Short Module Summary -Description: FIXME! Write a paragraph describing the functionality and the \ - purpose of this module. The description should contain multiple sentences. -URL: http://example.com/FIXME +Label: Legacy specimen tracking system +Description: A system for tracking specimens as part of a study. \ + This system has been deprecated, replaced by LabKey Sample Manager. +URL: https://www.labkey.com/products-services/sample-management-software/ License: Apache 2.0 LicenseURL: http://www.apache.org/licenses/LICENSE-2.0 diff --git a/specimen/schemas/specimens.xsd b/specimen/schemas/specimens.xsd index 4a2a3dfe..416ad0ef 100644 --- a/specimen/schemas/specimens.xsd +++ b/specimen/schemas/specimens.xsd @@ -191,4 +191,11 @@ + + + + + + + diff --git a/specimen/src/org/labkey/specimen/SpecimenModule.java b/specimen/src/org/labkey/specimen/SpecimenModule.java index cc20db71..599dd30e 100644 --- a/specimen/src/org/labkey/specimen/SpecimenModule.java +++ b/specimen/src/org/labkey/specimen/SpecimenModule.java @@ -22,24 +22,19 @@ import org.labkey.api.audit.AuditLogService; import org.labkey.api.data.Container; import org.labkey.api.data.ContainerManager; -import org.labkey.api.exp.property.PropertyService; import org.labkey.api.module.ModuleContext; import org.labkey.api.module.SpringModule; import org.labkey.api.security.roles.RoleManager; import org.labkey.api.specimen.SpecimenRequestManager; import org.labkey.api.specimen.SpecimensPage; import org.labkey.api.specimen.importer.SpecimenImporter; -import org.labkey.api.specimen.model.AdditiveTypeDomainKind; -import org.labkey.api.specimen.model.DerivativeTypeDomainKind; -import org.labkey.api.specimen.model.PrimaryTypeDomainKind; -import org.labkey.api.specimen.model.SpecimenDomainKind; -import org.labkey.api.specimen.model.SpecimenEventDomainKind; import org.labkey.api.specimen.model.SpecimenRequestEventType; -import org.labkey.api.specimen.model.VialDomainKind; +import org.labkey.api.specimen.view.SpecimenRequestNotificationEmailTemplate; import org.labkey.api.study.SpecimenService; import org.labkey.api.study.StudyService; import org.labkey.api.study.importer.SimpleStudyImporterRegistry; import org.labkey.api.study.writer.SimpleStudyWriterRegistry; +import org.labkey.api.util.emailTemplate.EmailTemplateService; import org.labkey.api.view.HttpView; import org.labkey.api.view.WebPartFactory; import org.labkey.specimen.action.SpecimenApiController; @@ -100,13 +95,6 @@ public boolean hasScripts() @Override protected void init() { - PropertyService.get().registerDomainKind(new AdditiveTypeDomainKind()); - PropertyService.get().registerDomainKind(new DerivativeTypeDomainKind()); - PropertyService.get().registerDomainKind(new PrimaryTypeDomainKind()); - PropertyService.get().registerDomainKind(new SpecimenDomainKind()); - PropertyService.get().registerDomainKind(new SpecimenEventDomainKind()); - PropertyService.get().registerDomainKind(new VialDomainKind()); - // Register early so these roles are available to Java code at upgrade time RoleManager.registerRole(new SpecimenCoordinatorRole()); RoleManager.registerRole(new SpecimenRequesterRole()); @@ -118,6 +106,7 @@ protected void init() // Register early -- some modules don't declare a runtime dependency on specimen module, but will use the // service if it's available SpecimenService.setInstance(new SpecimenServiceImpl()); + EmailTemplateService.get().registerTemplate(SpecimenRequestNotificationEmailTemplate.class); } @Override diff --git a/specimen/src/org/labkey/specimen/SpecimenServiceImpl.java b/specimen/src/org/labkey/specimen/SpecimenServiceImpl.java index b7101292..fb4cff42 100644 --- a/specimen/src/org/labkey/specimen/SpecimenServiceImpl.java +++ b/specimen/src/org/labkey/specimen/SpecimenServiceImpl.java @@ -39,7 +39,6 @@ import org.labkey.api.specimen.SpecimenManagerNew; import org.labkey.api.specimen.SpecimenSchema; import org.labkey.api.specimen.Vial; -import org.labkey.api.specimen.actions.AutoCompleteAction; import org.labkey.api.specimen.importer.SimpleSpecimenImporter; import org.labkey.api.specimen.importer.SpecimenColumn; import org.labkey.api.specimen.model.SpecimenTablesProvider; @@ -258,18 +257,6 @@ public Set getSampleInfo(Container studyContainer, User user, return Collections.singleton(new StudyParticipantVisit(studyContainer, null, participantId, visit, null)); } - @Override - public ActionURL getCompletionURL(Container studyContainer, SpecimenService.CompletionType type) - { - if (studyContainer == null) - return null; - - ActionURL url = new ActionURL(AutoCompleteAction.class, studyContainer); - url.addParameter("type", type.name()); - url.addParameter("prefix", ""); - return url; - } - @Override public Set> getSampleInfo(Container studyContainer, User user, boolean truncateTime) { diff --git a/specimen/src/org/labkey/specimen/action/SpecimenApiController.java b/specimen/src/org/labkey/specimen/action/SpecimenApiController.java index 39963bec..b4265cc9 100644 --- a/specimen/src/org/labkey/specimen/action/SpecimenApiController.java +++ b/specimen/src/org/labkey/specimen/action/SpecimenApiController.java @@ -23,7 +23,6 @@ import org.labkey.api.action.MutatingApiAction; import org.labkey.api.action.ReadOnlyApiAction; import org.labkey.api.action.SpringActionController; -import org.labkey.api.annotations.Migrate; import org.labkey.api.data.Container; import org.labkey.api.security.ActionNames; import org.labkey.api.security.RequiresPermission; @@ -499,7 +498,7 @@ public ApiResponse execute(VialRequestForm vialRequestForm, BindException errors @RequiresPermission(RequestSpecimensPermission.class) @ApiVersion(9.1) - @Migrate // TODO: Rename and adjust LABKEY.Specimen.AddSamplesToRequest() in labkey-api-js, then remove @ActionNames annotation + // labkey-js-api now calls addSpecimensToRequest.api, but older versions still call addSamplesToRequest.api @ActionNames("addSpecimensToRequest,addSamplesToRequest") public class AddSpecimensToRequestAction extends MutatingApiAction { diff --git a/study/test/src/org/labkey/test/tests/study/SpecimenMergeTest.java b/study/test/src/org/labkey/test/tests/study/SpecimenMergeTest.java index 26378a01..c4fa5b20 100644 --- a/study/test/src/org/labkey/test/tests/study/SpecimenMergeTest.java +++ b/study/test/src/org/labkey/test/tests/study/SpecimenMergeTest.java @@ -110,7 +110,6 @@ protected void setUpSteps() _containerHelper.createSubfolder(PROJECT_NAME, PROJECT_NAME, FOLDER_NAME, "Study", null); _containerHelper.enableModule("Specimen"); clickButton("Create Study"); - click(Locator.radioButtonByNameAndValue("simpleRepository", "true")); clickButton("Create Study"); setPipelineRoot(_studyDataRoot); From c510a30095aa64a2d8b5e4edfdd8b2c140f8496d Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Tue, 16 Feb 2021 17:57:34 -0800 Subject: [PATCH 330/494] Run sans specimen module Original PR: https://github.com/LabKey/testAutomation/pull/637 --- src/org/labkey/test/tests/AliquotTest.java | 5 ++--- .../test/tests/CustomizeEmailTemplateTest.java | 2 +- src/org/labkey/test/tests/SpecimenExportTest.java | 15 +-------------- .../test/tests/SpecimenMultipleImportTest.java | 2 +- src/org/labkey/test/tests/SpecimenTest.java | 5 ++--- 5 files changed, 7 insertions(+), 22 deletions(-) diff --git a/src/org/labkey/test/tests/AliquotTest.java b/src/org/labkey/test/tests/AliquotTest.java index 6e18e7f7..ced6b047 100644 --- a/src/org/labkey/test/tests/AliquotTest.java +++ b/src/org/labkey/test/tests/AliquotTest.java @@ -66,12 +66,11 @@ protected void doCreateSteps() clickButton("Create Study"); setFormElement(Locator.name("label"), getStudyLabel()); - click(Locator.radioButtonByNameAndValue("simpleRepository", "false")); clickButton("Create Study"); + _studyHelper.setupAdvancedRepositoryType(); + setupRequestabilityRules(); setPipelineRoot(StudyHelper.getPipelinePath()); - - setupRequestabilityRules(); startSpecimenImport(1); waitForSpecimenImport(); _studyHelper.createCustomParticipantGroup(getProjectName(), getFolderName(), "Category1", "Participant", null, false, PTIDS[0], PTIDS[1]); diff --git a/src/org/labkey/test/tests/CustomizeEmailTemplateTest.java b/src/org/labkey/test/tests/CustomizeEmailTemplateTest.java index 08f3dc5b..b0f3552f 100644 --- a/src/org/labkey/test/tests/CustomizeEmailTemplateTest.java +++ b/src/org/labkey/test/tests/CustomizeEmailTemplateTest.java @@ -93,8 +93,8 @@ protected void doCreateSteps() clickButton("Create Study"); setFormElement(Locator.name("label"), getStudyLabel()); - click(Locator.radioButtonByNameAndValue("simpleRepository", "false")); clickButton("Create Study"); + _studyHelper.setupAdvancedRepositoryType(); setPipelineRoot(StudyHelper.getPipelinePath()); startSpecimenImport(1); diff --git a/src/org/labkey/test/tests/SpecimenExportTest.java b/src/org/labkey/test/tests/SpecimenExportTest.java index 551b3f99..bd25b311 100644 --- a/src/org/labkey/test/tests/SpecimenExportTest.java +++ b/src/org/labkey/test/tests/SpecimenExportTest.java @@ -63,15 +63,14 @@ protected void doCreateSteps() clickButton("Create Study"); setFormElement(Locator.name("label"), getStudyLabel()); - click(Locator.radioButtonByNameAndValue("simpleRepository", "false")); clickButton("Create Study"); + _studyHelper.setupRepositoryType(true, true, true); setPipelineRoot(StudyHelper.getPipelinePath()); startSpecimenImport(1); waitForSpecimenImport(); setPipelineRootToDefault(); - setupRepositoryType(); setupRequestStatuses(); setupActorsAndGroups(); setupDefaultRequirements(); @@ -95,18 +94,6 @@ protected void doVerifySteps() verifySpecimenSettingsInArchive(); } - private void setupRepositoryType() - { - log("Setup specimen repository type settings"); - clickTab("Manage"); - clickAndWait(Locator.linkWithText("Change Repository Type")); - waitForElement(Locator.tagContainingText("h3","Manage Repository Settings")); - checkRadioButton(Locator.radioButtonByName("simple").index(1)); // Advanced repository type - checkRadioButton(Locator.radioButtonByName("specimenDataEditable").index(1)); // Editable specimen data - checkRadioButton(Locator.radioButtonByName("enableRequests").index(0)); // Enabled specimen requests - clickButton("Submit"); - } - private void setupWebpartGroupings() { log("Setup specimen webpart groupings"); diff --git a/src/org/labkey/test/tests/SpecimenMultipleImportTest.java b/src/org/labkey/test/tests/SpecimenMultipleImportTest.java index 2e007122..b0177280 100644 --- a/src/org/labkey/test/tests/SpecimenMultipleImportTest.java +++ b/src/org/labkey/test/tests/SpecimenMultipleImportTest.java @@ -60,8 +60,8 @@ protected void doCreateSteps() clickButton("Create Study"); setFormElement(Locator.name("label"), getStudyLabel()); - click(Locator.radioButtonByNameAndValue("simpleRepository", "false")); clickButton("Create Study"); + _studyHelper.setupAdvancedRepositoryType(); setPipelineRoot(StudyHelper.getPipelinePath()); diff --git a/src/org/labkey/test/tests/SpecimenTest.java b/src/org/labkey/test/tests/SpecimenTest.java index a0f1400f..57753e58 100644 --- a/src/org/labkey/test/tests/SpecimenTest.java +++ b/src/org/labkey/test/tests/SpecimenTest.java @@ -103,12 +103,11 @@ protected void doCreateSteps() clickButton("Create Study"); setFormElement(Locator.name("label"), getStudyLabel()); - click(Locator.radioButtonByNameAndValue("simpleRepository", "false")); clickButton("Create Study"); + _studyHelper.setupAdvancedRepositoryType(); + setupRequestabilityRules(); setPipelineRoot(StudyHelper.getPipelinePath()); - - setupRequestabilityRules(); startSpecimenImport(1); waitForSpecimenImport(); checkSpecimenReport(); From b0f1a06180a3a2d91fcb311e5a0442ea9cdcfb46 Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Fri, 19 Feb 2021 17:05:27 -0800 Subject: [PATCH 331/494] Conditionalize specimen setup & verification in tests Original PR: https://github.com/LabKey/platform/pull/2005 --- .../org/labkey/specimen/pipeline/FileAnalysisSpecimenTask.java | 1 + .../src/org/labkey/specimen/pipeline/StandaloneSpecimenTask.java | 1 + 2 files changed, 2 insertions(+) diff --git a/specimen/src/org/labkey/specimen/pipeline/FileAnalysisSpecimenTask.java b/specimen/src/org/labkey/specimen/pipeline/FileAnalysisSpecimenTask.java index 9e12b126..73f43a66 100644 --- a/specimen/src/org/labkey/specimen/pipeline/FileAnalysisSpecimenTask.java +++ b/specimen/src/org/labkey/specimen/pipeline/FileAnalysisSpecimenTask.java @@ -24,6 +24,7 @@ import java.io.IOException; import java.util.Date; +// Registered by the specimen module (specimenContext.xml), so specimen module is always present when this code is invoked. public class FileAnalysisSpecimenTask extends AbstractSpecimenTask { public static final String MERGE_SPECIMEN = "mergeSpecimen"; diff --git a/specimen/src/org/labkey/specimen/pipeline/StandaloneSpecimenTask.java b/specimen/src/org/labkey/specimen/pipeline/StandaloneSpecimenTask.java index ec0fb435..16895246 100644 --- a/specimen/src/org/labkey/specimen/pipeline/StandaloneSpecimenTask.java +++ b/specimen/src/org/labkey/specimen/pipeline/StandaloneSpecimenTask.java @@ -27,6 +27,7 @@ */ // This task is used to import specimen archives directly via the pipeline ui. SpecimenBatch is the associated pipeline job. +// Registered by the specimen module (specimenContext.xml), so specimen module is always present when this code is invoked. public class StandaloneSpecimenTask extends AbstractSpecimenTask { private StandaloneSpecimenTask(Factory factory, PipelineJob job) From 07dab0b16f9e1b666b81dab0891fdf75e9038232 Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Sat, 20 Feb 2021 09:30:59 -0800 Subject: [PATCH 332/494] Add controller alias to match @labkey/api expectations Original PR: https://github.com/LabKey/platform/pull/2013 --- specimen/src/org/labkey/specimen/SpecimenModule.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specimen/src/org/labkey/specimen/SpecimenModule.java b/specimen/src/org/labkey/specimen/SpecimenModule.java index 599dd30e..d173f4cf 100644 --- a/specimen/src/org/labkey/specimen/SpecimenModule.java +++ b/specimen/src/org/labkey/specimen/SpecimenModule.java @@ -101,7 +101,7 @@ protected void init() AttachmentService.get().registerAttachmentType(SpecimenRequestEventType.get()); - addController("specimen-api", SpecimenApiController.class, "study-samples-api"); + addController("specimen-api", SpecimenApiController.class, "study-samples-api", "specimens-api"); // Register early -- some modules don't declare a runtime dependency on specimen module, but will use the // service if it's available From 24b2dfcb027eaced0d3f7aa555725509b440f414 Mon Sep 17 00:00:00 2001 From: Alan Vezina Date: Tue, 11 May 2021 18:33:00 -0500 Subject: [PATCH 333/494] FormSchema, Updated Filewatcher UI Original PR: https://github.com/LabKey/platform/pull/2234 * Add FormSchema, AbstractField, TextField, TextAreaField, NumberField, CheckboxField, SelecField, RadioField, Option classes * Add getFormSchema to PipelineJobService * Add getFormSchema, getCustomFieldsFormSchema to FileAnalysisTaskPipeline * Add getCustomFields to FileAnalysisTaskPipelineSettings * Add CreatePipelineTrigger React component * Refactor createPipelineTrigger.jsp to use CreatePipelineTrigger component * Add custom fields to study, specimen, and list FileWatchers --- .../WEB-INF/specimen/specimenContext.xml | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/specimen/webapp/WEB-INF/specimen/specimenContext.xml b/specimen/webapp/WEB-INF/specimen/specimenContext.xml index c6a80365..55da46f9 100644 --- a/specimen/webapp/WEB-INF/specimen/specimenContext.xml +++ b/specimen/webapp/WEB-INF/specimen/specimenContext.xml @@ -64,6 +64,29 @@ + + + + + + + + + + + + + + + + + + + + + + + From 17f4da67a9a2aac9fa92ece3eebe718d997850d6 Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Tue, 1 Jun 2021 13:44:17 -0700 Subject: [PATCH 334/494] Move more specimen code to specimen module Original PR: https://github.com/LabKey/platform/pull/2305 --- .../org/labkey/specimen/SpecimenModule.java | 116 +- .../labkey/specimen/SpecimenServiceImpl.java | 22 +- .../actions/ReportConfigurationBean.java | 128 ++ .../actions/ShowGroupMembersAction.java | 275 ++++ .../specimen/actions/ShowSearchAction.java | 181 +++ .../actions/ShowUploadSpecimensAction.java | 380 +++++ .../SpecimenApiController.java | 71 +- .../specimen/actions/SpecimenController2.java | 1414 +++++++++++++++++ .../actions/SpecimenReportActions.java | 128 ++ .../specimen/pipeline/SpecimenBatch.java | 101 ++ .../specimen/pipeline/SpecimenPipeline.java | 57 + .../specimen/pipeline/SpecimenReloadJob.java | 1 - .../report/RequestSummaryByVisitType.java | 27 + .../specimen/report/SpecimenCountSummary.java | 60 + .../report/SpecimenDetailQueryHelper.java | 34 + .../report/SpecimenReportCellData.java | 27 + .../report/SpecimenReportExcelWriter.java | 154 ++ .../report/SpecimenReportManager.java | 377 +++++ .../specimen/report/SpecimenReportTitle.java | 55 + .../report/SpecimenTypeBeanProperty.java | 32 + .../specimen/report/SpecimenTypeLevel.java | 83 + .../report/SpecimenTypeVisitReport.java | 168 ++ .../specimen/report/SpecimenVisitReport.java | 427 +++++ .../report/SpecimenVisitReportAction.java | 60 + .../report/SpecimenVisitReportParameters.java | 544 +++++++ .../report/SummaryByVisitParticipant.java | 27 + .../specimen/report/SummaryByVisitType.java | 62 + .../participant/ParticipantSiteReport.java | 45 + .../ParticipantSiteReportFactory.java | 131 ++ .../ParticipantSummaryReportFactory.java | 69 + .../ParticipantTypeReportFactory.java | 218 +++ .../participant/ParticipantVisitReport.java | 124 ++ .../request/BaseRequestReportFactory.java | 71 + .../RequestEnrollmentLocationReport.java | 51 + .../RequestEnrollmentSiteReportFactory.java | 132 ++ .../report/request/RequestLocationReport.java | 49 + .../request/RequestLocationReportFactory.java | 144 ++ .../request/RequestParticipantReport.java | 120 ++ .../RequestParticipantReportFactory.java | 167 ++ .../report/request/RequestReport.java | 47 + .../report/request/RequestReportFactory.java | 85 + .../report/specimentype/TypeCohortReport.java | 51 + .../specimentype/TypeCohortReportFactory.java | 126 ++ .../TypeParticipantReportFactory.java | 156 ++ .../specimentype/TypeReportFactory.java | 62 + .../TypeSummaryReportFactory.java | 62 + .../security/roles/AbstractSpecimenRole.java | 2 +- .../view/SpecimenReportWebPartFactory.java | 4 +- .../view/SpecimenToolsWebPartFactory.java | 6 +- .../labkey/specimen/view/autoReportList.jsp | 4 +- .../view/configRequestabilityRules.jsp | 6 +- .../view/customizeSpecimenReportWebPart.jsp | 4 +- .../org/labkey/specimen/view/groupMembers.jsp | 86 + .../labkey/specimen/view/importSpecimens.jsp | 123 ++ .../specimen/view/manageDefaultReqs.jsp | 233 +++ .../labkey/specimen/view/manageDisplay.jsp | 107 ++ .../specimen/view/manageNotifications.jsp | 279 ++++ .../view/manageRepositorySettings.jsp | 113 ++ .../specimen/view/manageSpecimenWebPart.jsp | 239 +++ .../specimen/view/selectSpecimenProvider.jsp | 48 + .../specimen/view/specimenVisitReport.jsp | 6 +- .../specimen/view/uploadSimpleSpecimens.jsp | 154 ++ .../specimen/view/viewRequestsHeader.jsp | 58 + .../WEB-INF/specimen/specimenContext.xml | 2 +- 64 files changed, 8362 insertions(+), 33 deletions(-) create mode 100644 specimen/src/org/labkey/specimen/actions/ReportConfigurationBean.java create mode 100644 specimen/src/org/labkey/specimen/actions/ShowGroupMembersAction.java create mode 100644 specimen/src/org/labkey/specimen/actions/ShowSearchAction.java create mode 100644 specimen/src/org/labkey/specimen/actions/ShowUploadSpecimensAction.java rename specimen/src/org/labkey/specimen/{action => actions}/SpecimenApiController.java (94%) create mode 100644 specimen/src/org/labkey/specimen/actions/SpecimenController2.java create mode 100644 specimen/src/org/labkey/specimen/actions/SpecimenReportActions.java create mode 100644 specimen/src/org/labkey/specimen/pipeline/SpecimenBatch.java create mode 100644 specimen/src/org/labkey/specimen/pipeline/SpecimenPipeline.java create mode 100644 specimen/src/org/labkey/specimen/report/RequestSummaryByVisitType.java create mode 100644 specimen/src/org/labkey/specimen/report/SpecimenCountSummary.java create mode 100644 specimen/src/org/labkey/specimen/report/SpecimenDetailQueryHelper.java create mode 100644 specimen/src/org/labkey/specimen/report/SpecimenReportCellData.java create mode 100644 specimen/src/org/labkey/specimen/report/SpecimenReportExcelWriter.java create mode 100644 specimen/src/org/labkey/specimen/report/SpecimenReportManager.java create mode 100644 specimen/src/org/labkey/specimen/report/SpecimenReportTitle.java create mode 100644 specimen/src/org/labkey/specimen/report/SpecimenTypeBeanProperty.java create mode 100644 specimen/src/org/labkey/specimen/report/SpecimenTypeLevel.java create mode 100644 specimen/src/org/labkey/specimen/report/SpecimenTypeVisitReport.java create mode 100644 specimen/src/org/labkey/specimen/report/SpecimenVisitReport.java create mode 100644 specimen/src/org/labkey/specimen/report/SpecimenVisitReportAction.java create mode 100644 specimen/src/org/labkey/specimen/report/SpecimenVisitReportParameters.java create mode 100644 specimen/src/org/labkey/specimen/report/SummaryByVisitParticipant.java create mode 100644 specimen/src/org/labkey/specimen/report/SummaryByVisitType.java create mode 100644 specimen/src/org/labkey/specimen/report/participant/ParticipantSiteReport.java create mode 100644 specimen/src/org/labkey/specimen/report/participant/ParticipantSiteReportFactory.java create mode 100644 specimen/src/org/labkey/specimen/report/participant/ParticipantSummaryReportFactory.java create mode 100644 specimen/src/org/labkey/specimen/report/participant/ParticipantTypeReportFactory.java create mode 100644 specimen/src/org/labkey/specimen/report/participant/ParticipantVisitReport.java create mode 100644 specimen/src/org/labkey/specimen/report/request/BaseRequestReportFactory.java create mode 100644 specimen/src/org/labkey/specimen/report/request/RequestEnrollmentLocationReport.java create mode 100644 specimen/src/org/labkey/specimen/report/request/RequestEnrollmentSiteReportFactory.java create mode 100644 specimen/src/org/labkey/specimen/report/request/RequestLocationReport.java create mode 100644 specimen/src/org/labkey/specimen/report/request/RequestLocationReportFactory.java create mode 100644 specimen/src/org/labkey/specimen/report/request/RequestParticipantReport.java create mode 100644 specimen/src/org/labkey/specimen/report/request/RequestParticipantReportFactory.java create mode 100644 specimen/src/org/labkey/specimen/report/request/RequestReport.java create mode 100644 specimen/src/org/labkey/specimen/report/request/RequestReportFactory.java create mode 100644 specimen/src/org/labkey/specimen/report/specimentype/TypeCohortReport.java create mode 100644 specimen/src/org/labkey/specimen/report/specimentype/TypeCohortReportFactory.java create mode 100644 specimen/src/org/labkey/specimen/report/specimentype/TypeParticipantReportFactory.java create mode 100644 specimen/src/org/labkey/specimen/report/specimentype/TypeReportFactory.java create mode 100644 specimen/src/org/labkey/specimen/report/specimentype/TypeSummaryReportFactory.java create mode 100644 specimen/src/org/labkey/specimen/view/groupMembers.jsp create mode 100644 specimen/src/org/labkey/specimen/view/importSpecimens.jsp create mode 100644 specimen/src/org/labkey/specimen/view/manageDefaultReqs.jsp create mode 100644 specimen/src/org/labkey/specimen/view/manageDisplay.jsp create mode 100644 specimen/src/org/labkey/specimen/view/manageNotifications.jsp create mode 100644 specimen/src/org/labkey/specimen/view/manageRepositorySettings.jsp create mode 100644 specimen/src/org/labkey/specimen/view/manageSpecimenWebPart.jsp create mode 100644 specimen/src/org/labkey/specimen/view/selectSpecimenProvider.jsp create mode 100644 specimen/src/org/labkey/specimen/view/uploadSimpleSpecimens.jsp create mode 100644 specimen/src/org/labkey/specimen/view/viewRequestsHeader.jsp diff --git a/specimen/src/org/labkey/specimen/SpecimenModule.java b/specimen/src/org/labkey/specimen/SpecimenModule.java index d173f4cf..a47a0aa5 100644 --- a/specimen/src/org/labkey/specimen/SpecimenModule.java +++ b/specimen/src/org/labkey/specimen/SpecimenModule.java @@ -24,10 +24,13 @@ import org.labkey.api.data.ContainerManager; import org.labkey.api.module.ModuleContext; import org.labkey.api.module.SpringModule; +import org.labkey.api.pipeline.PipelineService; import org.labkey.api.security.roles.RoleManager; +import org.labkey.api.specimen.SpecimenMigrationService; import org.labkey.api.specimen.SpecimenRequestManager; import org.labkey.api.specimen.SpecimensPage; import org.labkey.api.specimen.importer.SpecimenImporter; +import org.labkey.api.specimen.model.SpecimenRequestEvent; import org.labkey.api.specimen.model.SpecimenRequestEventType; import org.labkey.api.specimen.view.SpecimenRequestNotificationEmailTemplate; import org.labkey.api.study.SpecimenService; @@ -35,14 +38,21 @@ import org.labkey.api.study.importer.SimpleStudyImporterRegistry; import org.labkey.api.study.writer.SimpleStudyWriterRegistry; import org.labkey.api.util.emailTemplate.EmailTemplateService; +import org.labkey.api.view.ActionURL; import org.labkey.api.view.HttpView; import org.labkey.api.view.WebPartFactory; -import org.labkey.specimen.action.SpecimenApiController; +import org.labkey.specimen.actions.ShowGroupMembersAction; +import org.labkey.specimen.actions.ShowSearchAction; +import org.labkey.specimen.actions.ShowUploadSpecimensAction; +import org.labkey.specimen.actions.SpecimenApiController; +import org.labkey.specimen.actions.SpecimenController2; +import org.labkey.specimen.actions.SpecimenController2.OverviewAction; import org.labkey.specimen.importer.DefaultSpecimenImportStrategyFactory; import org.labkey.specimen.importer.SpecimenSchemaImporter; import org.labkey.specimen.importer.SpecimenSettingsImporter; import org.labkey.specimen.pipeline.SampleMindedTransform; import org.labkey.specimen.pipeline.SampleMindedTransformTask; +import org.labkey.specimen.pipeline.SpecimenPipeline; import org.labkey.specimen.security.roles.SpecimenCoordinatorRole; import org.labkey.specimen.security.roles.SpecimenRequesterRole; import org.labkey.specimen.view.SpecimenReportWebPartFactory; @@ -101,13 +111,114 @@ protected void init() AttachmentService.get().registerAttachmentType(SpecimenRequestEventType.get()); + addController("specimen2", SpecimenController2.class); addController("specimen-api", SpecimenApiController.class, "study-samples-api", "specimens-api"); // Register early -- some modules don't declare a runtime dependency on specimen module, but will use the // service if it's available SpecimenService.setInstance(new SpecimenServiceImpl()); EmailTemplateService.get().registerTemplate(SpecimenRequestNotificationEmailTemplate.class); - } + + SpecimenMigrationService.setInstance(new SpecimenMigrationService() + { + @Override + public ActionURL getSpecimenRequestEventDownloadURL(SpecimenRequestEvent event, String name) + { + return SpecimenController2.getDownloadURL(event, name); + } + + @Override + public ActionURL getOverviewURL(Container c) + { + return new ActionURL(OverviewAction.class, c); + } + + @Override + public ActionURL getShowGroupMembersURL(Container c, int rowId, @Nullable Integer locationId, @Nullable ActionURL returnUrl) + { + ActionURL url = new ActionURL(ShowGroupMembersAction.class, c); + url.addParameter("id", Integer.toString(rowId)); + if (locationId != null) + url.addParameter("locationId", locationId); + if (returnUrl != null) + url.addReturnURL(returnUrl); + + return url; + } + + @Override + public ActionURL getUploadSpecimensURL(Container c) + { + return new ActionURL(ShowUploadSpecimensAction.class, c); + } + + @Override + public ActionURL getAutoReportListURL(Container c) + { + return new ActionURL(SpecimenController2.AutoReportListAction.class, c); + } + + @Override + public ActionURL getCompleteSpecimenURL(Container c, String type) + { + return new ActionURL(SpecimenController2.CompleteSpecimenAction.class, c).addParameter("type", type); + } + + @Override + public ActionURL getShowSearchURL(Container c, boolean showVials) + { + return new ActionURL(ShowSearchAction.class, c).addParameter("showVials", showVials); + } + + @Override + public ActionURL getSpecimenRequestConfigRequiredURL(Container c) + { + return new ActionURL(SpecimenController2.SpecimenRequestConfigRequiredAction.class, c); + } + + @Override + public ActionURL getConfigureRequestabilityRulesURL(Container c) + { + return new ActionURL(SpecimenController2.ConfigureRequestabilityRulesAction.class, c); + } + + @Override + public ActionURL getViewRequestsURL(Container c) + { + return new ActionURL(SpecimenController2.ViewRequestsAction.class, c); + } + + @Override + public ActionURL getSpecimenEventsURL(Container c, ActionURL returnURL) + { + return new ActionURL(SpecimenController2.SpecimenEventsAction.class, c).addReturnURL(returnURL); + } + + @Override + public ActionURL getManageNotificationsURL(Container c) + { + return new ActionURL(SpecimenController2.ManageNotificationsAction.class, c); + } + + @Override + public ActionURL getManageDisplaySettings(Container c) + { + return new ActionURL(SpecimenController2.ManageDisplaySettingsAction.class, c); + } + + @Override + public ActionURL getManageRepositorySettingsURL(Container c) + { + return new ActionURL(SpecimenController2.ManageRepositorySettingsAction.class, c); + } + + @Override + public ActionURL getManageDefaultReqsSettingsURL(Container c) + { + return new ActionURL(SpecimenController2.ManageDefaultReqsAction.class, c); + } + }); + } @Override protected void startupAfterSpringConfig(ModuleContext moduleContext) @@ -118,6 +229,7 @@ protected void startupAfterSpringConfig(ModuleContext moduleContext) SpecimenService.get().registerSpecimenImportStrategyFactory(new DefaultSpecimenImportStrategyFactory()); AuditLogService.get().registerAuditType(new SpecimenCommentAuditProvider()); SpecimenService.get().registerSpecimenTransform(new SampleMindedTransform()); + PipelineService.get().registerPipelineProvider(new SpecimenPipeline(this)); SimpleStudyWriterRegistry.registerSimpleStudyWriterProvider(() -> List.of( new SpecimenSettingsWriter(), diff --git a/specimen/src/org/labkey/specimen/SpecimenServiceImpl.java b/specimen/src/org/labkey/specimen/SpecimenServiceImpl.java index fb4cff42..96f3073f 100644 --- a/specimen/src/org/labkey/specimen/SpecimenServiceImpl.java +++ b/specimen/src/org/labkey/specimen/SpecimenServiceImpl.java @@ -53,7 +53,9 @@ import org.labkey.api.util.Pair; import org.labkey.api.view.ActionURL; import org.labkey.api.view.ViewBackgroundInfo; +import org.labkey.specimen.actions.SpecimenController2; import org.labkey.specimen.pipeline.SpecimenReloadJob; +import org.springframework.web.servlet.mvc.Controller; import java.io.IOException; import java.util.ArrayList; @@ -389,13 +391,6 @@ public void registerSpecimenChangeListener(SpecimenChangeListener listener) _changeListeners.add(listener); } - @Override - public void fireSpecimensChanged(Container c, User user, Logger logger) - { - for (SpecimenChangeListener l : _changeListeners) - l.specimensChanged(c, user, logger); - } - private static final SpecimenTablesTemplate _specimenTablesTemplate = new DefaultSpecimenTablesTemplate(); @Nullable @@ -461,4 +456,17 @@ public void registerRequestCustomizer(SpecimenRequestCustomizer customizer) { _specimenRequestCustomizer = customizer; } + + @Override + public void fireSpecimensChanged(Container c, User user, Logger logger) + { + for (SpecimenChangeListener l : _changeListeners) + l.specimensChanged(c, user, logger); + } + + @Override + public Class getManageSpecimenWebPartActionClass() + { + return SpecimenController2.ManageSpecimenWebPartAction.class; + } } diff --git a/specimen/src/org/labkey/specimen/actions/ReportConfigurationBean.java b/specimen/src/org/labkey/specimen/actions/ReportConfigurationBean.java new file mode 100644 index 00000000..29620e6d --- /dev/null +++ b/specimen/src/org/labkey/specimen/actions/ReportConfigurationBean.java @@ -0,0 +1,128 @@ +package org.labkey.specimen.actions; + +import org.labkey.api.query.CustomView; +import org.labkey.api.query.QueryDefinition; +import org.labkey.api.query.QueryService; +import org.labkey.api.query.SchemaKey; +import org.labkey.api.query.UserSchema; +import org.labkey.api.specimen.SpecimenQuerySchema; +import org.labkey.specimen.report.SpecimenVisitReportParameters; +import org.labkey.api.specimen.settings.SettingsManager; +import org.labkey.api.study.Study; +import org.labkey.api.study.StudyService; +import org.labkey.api.view.ViewContext; +import org.labkey.specimen.report.participant.ParticipantSiteReportFactory; +import org.labkey.specimen.report.participant.ParticipantSummaryReportFactory; +import org.labkey.specimen.report.participant.ParticipantTypeReportFactory; +import org.labkey.specimen.report.request.RequestEnrollmentSiteReportFactory; +import org.labkey.specimen.report.request.RequestLocationReportFactory; +import org.labkey.specimen.report.request.RequestParticipantReportFactory; +import org.labkey.specimen.report.request.RequestReportFactory; +import org.labkey.specimen.report.specimentype.TypeCohortReportFactory; +import org.labkey.specimen.report.specimentype.TypeParticipantReportFactory; +import org.labkey.specimen.report.specimentype.TypeSummaryReportFactory; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public class ReportConfigurationBean +{ + private static final String COUNTS_BY_DERIVATIVE_TYPE_TITLE = "Collected Vials by Type and Timepoint"; + private static final String REQUESTS_BY_DERIVATIVE_TYPE_TITLE = "Requested Vials by Type and Timepoint"; + + private final Map> _reportFactories = new LinkedHashMap<>(); + private final int _uniqueId; + private final ViewContext _viewContext; + + private boolean _listView = true; + private boolean _hasReports = true; + + public ReportConfigurationBean(ViewContext viewContext) + { + Study study = StudyService.get().getStudy(viewContext.getContainer()); + _viewContext = viewContext; + registerReportFactory(COUNTS_BY_DERIVATIVE_TYPE_TITLE, new TypeSummaryReportFactory()); + registerReportFactory(COUNTS_BY_DERIVATIVE_TYPE_TITLE, new TypeParticipantReportFactory()); + if (StudyService.get().showCohorts(_viewContext.getContainer(), _viewContext.getUser())) + registerReportFactory(COUNTS_BY_DERIVATIVE_TYPE_TITLE, new TypeCohortReportFactory()); + if (study != null) + { + boolean enableSpecimenRequest = SettingsManager.get().getRepositorySettings(study.getContainer()).isEnableRequests(); + if (!study.isAncillaryStudy() && !study.isSnapshotStudy() && enableSpecimenRequest) + { + registerReportFactory(REQUESTS_BY_DERIVATIVE_TYPE_TITLE, new RequestReportFactory()); + registerReportFactory(REQUESTS_BY_DERIVATIVE_TYPE_TITLE, new RequestLocationReportFactory()); + registerReportFactory(REQUESTS_BY_DERIVATIVE_TYPE_TITLE, new RequestEnrollmentSiteReportFactory()); + registerReportFactory(REQUESTS_BY_DERIVATIVE_TYPE_TITLE, new RequestParticipantReportFactory()); + } + String subjectNoun = StudyService.get().getSubjectNounSingular(viewContext.getContainer()); + registerReportFactory("Collected Vials by " + subjectNoun + " by Timepoint", new ParticipantSummaryReportFactory()); + registerReportFactory("Collected Vials by " + subjectNoun + " by Timepoint", new ParticipantTypeReportFactory()); + registerReportFactory("Collected Vials by " + subjectNoun + " by Timepoint", new ParticipantSiteReportFactory()); + } + + _uniqueId = 0; + } + + // TODO: listView parameter is always false... remove? + public ReportConfigurationBean(SpecimenVisitReportParameters singleFactory, boolean listView, int uniqueId) + { + _listView = listView; + _viewContext = singleFactory.getViewContext(); + assert (_viewContext != null) : "Expected report factory to be instantiated by Spring."; + registerReportFactory(COUNTS_BY_DERIVATIVE_TYPE_TITLE, singleFactory); + _hasReports = StudyService.get().getStudy(_viewContext.getContainer()) != null && !singleFactory.getReports().isEmpty(); + _uniqueId = uniqueId; + } + + private void registerReportFactory(String category, SpecimenVisitReportParameters factory) + { + // we have to explicitly set the view context for these reports, since the factories aren't being newed-up by Spring in the usual way: + factory.setViewContext(_viewContext); + List factories = _reportFactories.get(category); + + if (factories == null) + { + factories = new ArrayList<>(); + _reportFactories.put(category, factories); + } + + factories.add(factory); + } + + public Set getCategories() + { + return _reportFactories.keySet(); + } + + public List getFactories(String category) + { + return _reportFactories.get(category); + } + + public boolean isListView() + { + return _listView; + } + + public Map getCustomViews(ViewContext context) + { + // 13485 - Use provider to handle NULL study + UserSchema schema = QueryService.get().getUserSchema(context.getUser(), context.getContainer(), SchemaKey.fromParts(SpecimenQuerySchema.SCHEMA_NAME)); + QueryDefinition def = QueryService.get().createQueryDefForTable(schema, "SpecimenDetail"); + return def.getCustomViews(context.getUser(), context.getRequest(), false, false); + } + + public boolean hasReports() + { + return _hasReports; + } + + public int getUniqueId() + { + return _uniqueId; + } +} diff --git a/specimen/src/org/labkey/specimen/actions/ShowGroupMembersAction.java b/specimen/src/org/labkey/specimen/actions/ShowGroupMembersAction.java new file mode 100644 index 00000000..5abde517 --- /dev/null +++ b/specimen/src/org/labkey/specimen/actions/ShowGroupMembersAction.java @@ -0,0 +1,275 @@ +/* + * 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.specimen.actions; + +import org.apache.commons.lang3.StringUtils; +import org.labkey.api.action.FormViewAction; +import org.labkey.api.action.SpringActionController; +import org.labkey.api.security.RequiresPermission; +import org.labkey.api.security.SecurityManager; +import org.labkey.api.security.SecurityUrls; +import org.labkey.api.security.User; +import org.labkey.api.security.UserManager; +import org.labkey.api.security.ValidEmail; +import org.labkey.api.security.permissions.UserManagementPermission; +import org.labkey.api.specimen.actions.IdForm; +import org.labkey.api.specimen.location.LocationImpl; +import org.labkey.api.specimen.location.LocationManager; +import org.labkey.api.specimen.model.SpecimenRequestActor; +import org.labkey.api.specimen.requirements.SpecimenRequestRequirementProvider; +import org.labkey.api.specimen.security.permissions.ManageSpecimenActorsPermission; +import org.labkey.api.study.SpecimenUrls; +import org.labkey.api.study.StudyUrls; +import org.labkey.api.util.PageFlowUtil; +import org.labkey.api.view.ActionURL; +import org.labkey.api.view.JspView; +import org.labkey.api.view.NavTree; +import org.labkey.api.view.NotFoundException; +import org.springframework.validation.BindException; +import org.springframework.validation.Errors; +import org.springframework.web.servlet.ModelAndView; + +import javax.servlet.ServletException; +import java.util.ArrayList; +import java.util.List; + +@RequiresPermission(ManageSpecimenActorsPermission.class) +public class ShowGroupMembersAction extends FormViewAction +{ + private SpecimenRequestActor _actor; + private LocationImpl _location; + + @Override + public void validateCommand(UpdateGroupForm target, Errors errors) + { + } + + @Override + public ModelAndView getView(UpdateGroupForm form, boolean reshow, BindException errors) + { + SpecimenRequestActor actor = getActor(form); + LocationImpl location = getLocation(form); + + if (actor == null) + throw new NotFoundException(); + + User[] members = actor.getMembers(location); + + return new JspView<>("/org/labkey/specimen/view/groupMembers.jsp", + new GroupMembersBean(actor, location, members, form.getReturnActionURL()), errors); + } + + @Override + public boolean handlePost(UpdateGroupForm form, BindException errors) throws Exception + { + String[] emailsToDelete = form.getDelete(); + SpecimenRequestActor actor = getActor(form); + LocationImpl location = getLocation(form); + + if (emailsToDelete != null && emailsToDelete.length > 0) + { + List invalidEmails = new ArrayList<>(); + List emails = SecurityManager.normalizeEmails(emailsToDelete, invalidEmails); + + if (invalidEmails.isEmpty()) + { + User[] users = new User[emails.size()]; + int i = 0; + for (ValidEmail email : emails) + { + users[i++] = UserManager.getUser(email); + assert users[i - 1] != null : email.getEmailAddress() + " is not associated with a user account."; + } + actor.removeMembers(location, users); + } + else + throw new ServletException("Invalid email address" + (invalidEmails.size() > 1 ? "es: " : ": ") + StringUtils.join(invalidEmails.toArray())); + } + + if (form.getNames() != null) + { + String[] names = form.getNames().split("\n"); + List invalidEmails = new ArrayList<>(); + List emails = SecurityManager.normalizeEmails(names, invalidEmails); + + for (String rawEmail : invalidEmails) + { + // Ignore lines of all whitespace, otherwise show an error. + if (!"".equals(rawEmail.trim())) + { + errors.reject(SpringActionController.ERROR_MSG, "Could not add user " + rawEmail.trim() + ": Invalid email address"); + } + } + + List newMembers = new ArrayList<>(); + + for (ValidEmail email : emails) + { + if (getUser().hasRootPermission(UserManagementPermission.class)) + { + SecurityManager.addUser(getViewContext(), email, form.isSendEmail(), null); + newMembers.add(UserManager.getUser(email)); + } + else + { + User user = UserManager.getUser(email); + + if (user == null) + { + errors.reject(SpringActionController.ERROR_MSG, email.getEmailAddress() + " is not a registered system user."); + } + else + { + newMembers.add(UserManager.getUser(email)); + } + } + } + + actor.addMembers(location, newMembers.toArray(new User[newMembers.size()])); + } + + return !errors.hasErrors(); + } + + + @Override + public ActionURL getSuccessURL(UpdateGroupForm form) + { + return form.getReturnActionURL(PageFlowUtil.urlProvider(SpecimenUrls.class).getManageActorsURL(getContainer())); + } + + @Override + public void addNavTrail(NavTree root) + { + PageFlowUtil.urlProvider(StudyUrls.class).addManageStudyNavTrail(root, getContainer(), getUser()); + + if (_location != null) + root.addChild("Manage Actors", PageFlowUtil.urlProvider(SpecimenUrls.class).getManageActorsURL(getContainer()).addParameter("showMemberSites", _actor.getRowId())); + else + root.addChild("Manage Actors", PageFlowUtil.urlProvider(SpecimenUrls.class).getManageActorsURL(getContainer())); + + String title = _actor.getLabel(); + if (_location != null) + title += ", " + _location.getLabel(); + + root.addChild(title); + } + + private SpecimenRequestActor getActor(UpdateGroupForm form) + { + if (_actor == null) + _actor = SpecimenRequestRequirementProvider.get().getActor(getContainer(), form.getId()); + + return _actor; + } + + private LocationImpl getLocation(UpdateGroupForm form) + { + if (_location == null && form.getLocationId() != null) + _location = LocationManager.get().getLocation(getContainer(), form.getLocationId()); + + return _location; + } + + public static class UpdateGroupForm extends IdForm + { + private Integer _locationId; + private String[] _delete; + private boolean _sendEmail; + private String _names; + + public Integer getLocationId() + { + return _locationId; + } + + public void setLocationId(Integer locationId) + { + _locationId = locationId; + } + + public String getNames() + { + return _names; + } + + public void setNames(String names) + { + _names = names; + } + + public boolean isSendEmail() + { + return _sendEmail; + } + + public void setSendEmail(boolean sendEmail) + { + _sendEmail = sendEmail; + } + + public String[] getDelete() + { + return _delete; + } + + public void setDelete(String[] delete) + { + _delete = delete; + } + } + + public static class GroupMembersBean + { + private final SpecimenRequestActor _actor; + private final LocationImpl _location; + private final User[] _members; + private final ActionURL _returnUrl; + + public GroupMembersBean(SpecimenRequestActor actor, LocationImpl location, User[] members, ActionURL returnUrl) + { + _actor = actor; + _location = location; + _members = members; + _returnUrl = returnUrl; + } + + public SpecimenRequestActor getActor() + { + return _actor; + } + + public User[] getMembers() + { + return _members; + } + + public LocationImpl getLocation() + { + return _location; + } + + public ActionURL getReturnUrl() + { + return _returnUrl; + } + + public ActionURL getCompleteUsersPrefix() + { + return PageFlowUtil.urlProvider(SecurityUrls.class).getCompleteUserURL(_actor.getContainer()); + } + } +} \ No newline at end of file diff --git a/specimen/src/org/labkey/specimen/actions/ShowSearchAction.java b/specimen/src/org/labkey/specimen/actions/ShowSearchAction.java new file mode 100644 index 00000000..587e7e73 --- /dev/null +++ b/specimen/src/org/labkey/specimen/actions/ShowSearchAction.java @@ -0,0 +1,181 @@ +/* + * 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.specimen.actions; + +import org.labkey.api.action.FormViewAction; +import org.labkey.api.data.CompareType; +import org.labkey.api.security.RequiresPermission; +import org.labkey.api.security.permissions.ReadPermission; +import org.labkey.api.specimen.SpecimenSearchBean; +import org.labkey.api.study.SpecimenUrls; +import org.labkey.api.study.Study; +import org.labkey.api.study.StudyService; +import org.labkey.api.study.StudyUrls; +import org.labkey.api.util.PageFlowUtil; +import org.labkey.api.view.ActionURL; +import org.labkey.api.view.JspView; +import org.labkey.api.view.NavTree; +import org.labkey.api.view.NotFoundException; +import org.springframework.validation.BindException; +import org.springframework.validation.Errors; +import org.springframework.web.servlet.ModelAndView; + +@RequiresPermission(ReadPermission.class) +public class ShowSearchAction extends FormViewAction +{ + private Study _study; + private String _title; + + @Override + public void validateCommand(SearchForm target, Errors errors) + { + } + + @Override + public ModelAndView getView(SearchForm form, boolean reshow, BindException errors) + { + _study = StudyService.get().getStudy(getContainer()); + if (null == _study) + throw new NotFoundException("No study exists in this folder."); + + _title = (form.isShowVials() ? "Individual Vial" : "Grouped Vial") + " Search"; + + SpecimenSearchBean bean = new SpecimenSearchBean(getViewContext(), form.isShowVials(), false); + bean.setAdvancedExpanded(form.isShowAdvanced()); + bean.init(getViewContext(), form.isShowVials(), true); + bean.setWebPartId(1); + setTitle(_title); + + return new JspView<>("/org/labkey/specimen/view/search.jsp", bean); + } + + @Override + public boolean handlePost(SearchForm form, BindException errors) + { + return true; + } + + @Override + public ActionURL getSuccessURL(SearchForm form) + { + ActionURL url = PageFlowUtil.urlProvider(SpecimenUrls.class).getSpecimensURL(getContainer()); + url.addParameter("showVials", Boolean.toString(form.isShowVials())); + for (ShowSearchAction.SearchForm.SearchParam param : form.getSearchParams()) + { + if (param.getCompareType() != null && param.getCompareType().length() > 0) + { + CompareType compare = CompareType.valueOf(param.getCompareType()); + if (!compare.isDataValueRequired() || (param.getValue() != null && param.getValue().length() > 0)) + url.addParameter(param.getColumnName() + "~" + compare.getPreferredUrlKey(), param.getValue()); + } + } + return url; + } + + @Override + public void addNavTrail(NavTree root) + { + setHelpTopic("specimenShopping"); + root.addChild(_study.getLabel(), PageFlowUtil.urlProvider(StudyUrls.class).getStudyOverviewURL(getContainer())); + root.addChild("Specimen Overview", new ActionURL(SpecimenController2.OverviewAction.class, getContainer())); + root.addChild(_title); + } + + public static class SearchForm extends ShowSearchForm + { + private boolean _showAdvanced = false; + private SearchParam[] _searchParams; + + public static class SearchParam + { + private String _compareType; + private String _value; + private String _columnName; + + public String getCompareType() + { + return _compareType; + } + + public void setCompareType(String compareType) + { + _compareType = compareType; + } + + public String getValue() + { + return _value; + } + + public void setValue(String value) + { + _value = value; + } + + public String getColumnName() + { + return _columnName; + } + + public void setColumnName(String columnName) + { + _columnName = columnName; + } + } + + public SearchForm() + { + _searchParams = new SearchParam[100]; + for (int i = 0; i < 100; i++) + _searchParams[i] = new SearchParam(); + } + + public SearchParam[] getSearchParams() + { + return _searchParams; + } + + public void setSearchParams(SearchParam[] searchParams) + { + _searchParams = searchParams; + } + + public boolean isShowAdvanced() + { + return _showAdvanced; + } + + public void setShowAdvanced(boolean showAdvanced) + { + _showAdvanced = showAdvanced; + } + } + + public static class ShowSearchForm + { + private boolean _showVials; + + public boolean isShowVials() + { + return _showVials; + } + + public void setShowVials(boolean showVials) + { + _showVials = showVials; + } + } +} diff --git a/specimen/src/org/labkey/specimen/actions/ShowUploadSpecimensAction.java b/specimen/src/org/labkey/specimen/actions/ShowUploadSpecimensAction.java new file mode 100644 index 00000000..38f791d4 --- /dev/null +++ b/specimen/src/org/labkey/specimen/actions/ShowUploadSpecimensAction.java @@ -0,0 +1,380 @@ +/* + * 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.specimen.actions; + +import org.apache.commons.beanutils.ConversionException; +import org.apache.commons.lang3.StringUtils; +import org.labkey.api.action.FormViewAction; +import org.labkey.api.action.SimpleViewAction; +import org.labkey.api.action.SpringActionController; +import org.labkey.api.collections.CaseInsensitiveHashMap; +import org.labkey.api.data.Container; +import org.labkey.api.data.RuntimeSQLException; +import org.labkey.api.pipeline.PipelineUrls; +import org.labkey.api.portal.ProjectUrls; +import org.labkey.api.reader.ColumnDescriptor; +import org.labkey.api.reader.TabLoader; +import org.labkey.api.security.RequiresPermission; +import org.labkey.api.security.User; +import org.labkey.api.security.permissions.AdminPermission; +import org.labkey.api.specimen.SpecimenManagerNew; +import org.labkey.api.specimen.importer.SimpleSpecimenImporter; +import org.labkey.api.specimen.settings.RepositorySettings; +import org.labkey.api.specimen.settings.SettingsManager; +import org.labkey.api.study.SpecimenUrls; +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.util.PageFlowUtil; +import org.labkey.api.util.Pair; +import org.labkey.api.view.ActionURL; +import org.labkey.api.view.HtmlView; +import org.labkey.api.view.HttpView; +import org.labkey.api.view.JspView; +import org.labkey.api.view.NavTree; +import org.springframework.validation.BindException; +import org.springframework.validation.Errors; +import org.springframework.web.servlet.ModelAndView; + +import java.io.IOException; +import java.sql.BatchUpdateException; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +@RequiresPermission(AdminPermission.class) +public class ShowUploadSpecimensAction extends FormViewAction +{ + @Override + public void validateCommand(UploadSpecimensForm form, Errors errors) + { + if (StringUtils.trimToNull(form.getTsv()) == null) + errors.reject(SpringActionController.ERROR_MSG, "Please supply data to upload"); + } + + @Override + public ModelAndView getView(UploadSpecimensForm form, boolean reshow, BindException errors) + { + Container container = getContainer(); + RepositorySettings settings = SettingsManager.get().getRepositorySettings(container); + if (!settings.isSimple()) + return HttpView.redirect(PageFlowUtil. urlProvider(PipelineUrls.class).urlBrowse(container)); + + boolean isEmpty = SpecimenManagerNew.get().isSpecimensEmpty(container, getUser()); + if (isEmpty) + { + form.setNoSpecimens(true); + form.setReplaceOrMerge("replace"); + } + return new JspView<>("/org/labkey/specimen/view/uploadSimpleSpecimens.jsp", form, errors); + } + + @Override + public boolean handlePost(UploadSpecimensForm form, BindException errors) throws Exception + { + Container container = getContainer(); + User user = getUser(); + Study study = StudyService.get().getStudy(getContainer()); + SimpleSpecimenImporter importer = new SimpleSpecimenImporter(container, user, + study != null ? study.getTimepointType() : TimepointType.DATE, + StudyService.get().getSubjectNounSingular(container)); + + TabLoader loader = new TabLoader(form.getTsv(), true); + Map columnAliases = new CaseInsensitiveHashMap<>(); + //Make sure we accept the labels + for (Map.Entry entry : importer.getColumnLabels().entrySet()) + columnAliases.put(entry.getValue(), entry.getKey()); + //And a few more aliases + columnAliases.put(StudyService.get().getSubjectColumnName(container), SimpleSpecimenImporter.PARTICIPANT_ID); + columnAliases.put(StudyService.get().getSubjectNounSingular(container), SimpleSpecimenImporter.PARTICIPANT_ID); + columnAliases.put("ParticipantId", SimpleSpecimenImporter.PARTICIPANT_ID); + columnAliases.put("Participant Id", SimpleSpecimenImporter.PARTICIPANT_ID); + columnAliases.put("Participant", SimpleSpecimenImporter.PARTICIPANT_ID); + columnAliases.put("Subject", SimpleSpecimenImporter.PARTICIPANT_ID); + columnAliases.put("SequenceNum", SimpleSpecimenImporter.VISIT); + columnAliases.put("Sequence Num", SimpleSpecimenImporter.VISIT); + columnAliases.put("Visit", SimpleSpecimenImporter.VISIT); + columnAliases.put("Timepoint Number", SimpleSpecimenImporter.VISIT); + columnAliases.put("specimenNumber", SimpleSpecimenImporter.SAMPLE_ID); + columnAliases.put("specimen Number", SimpleSpecimenImporter.SAMPLE_ID); + columnAliases.put("totalVolume", SimpleSpecimenImporter.VOLUME); + columnAliases.put("total Volume", SimpleSpecimenImporter.VOLUME); + columnAliases.put("volumeUnits", SimpleSpecimenImporter.UNITS); + columnAliases.put("volume Units", SimpleSpecimenImporter.UNITS); + columnAliases.put("primaryType", SimpleSpecimenImporter.PRIMARY_SPECIMEN_TYPE); + columnAliases.put("primary Type", SimpleSpecimenImporter.PRIMARY_SPECIMEN_TYPE); + columnAliases.put("additive", SimpleSpecimenImporter.ADDITIVE_TYPE); + columnAliases.put("additiveType", SimpleSpecimenImporter.ADDITIVE_TYPE); + columnAliases.put("additive Type", SimpleSpecimenImporter.ADDITIVE_TYPE); + columnAliases.put("derivative", SimpleSpecimenImporter.DERIVIATIVE_TYPE); + columnAliases.put("derivativeType", SimpleSpecimenImporter.DERIVIATIVE_TYPE); + columnAliases.put("derivative Type", SimpleSpecimenImporter.DERIVIATIVE_TYPE); + columnAliases.put("drawTimestamp", SimpleSpecimenImporter.DRAW_TIMESTAMP); + columnAliases.put("Draw Date", SimpleSpecimenImporter.DRAW_TIMESTAMP); + columnAliases.put("Date", SimpleSpecimenImporter.DRAW_TIMESTAMP); + columnAliases.put("draw Timestamp", SimpleSpecimenImporter.DRAW_TIMESTAMP); + columnAliases.put("Vial Id", SimpleSpecimenImporter.VIAL_ID); + columnAliases.put("globalUniqueId", SimpleSpecimenImporter.VIAL_ID); + columnAliases.put("global Unique Id", SimpleSpecimenImporter.VIAL_ID); + columnAliases.put("Units", SimpleSpecimenImporter.UNITS); + columnAliases.put("Specimen Type", SimpleSpecimenImporter.PRIMARY_SPECIMEN_TYPE); + + //Remember whether we used a different header so we can put up error messages that make sense + Map labels = new HashMap<>(); + ColumnDescriptor[] columns; + try + { + columns = loader.getColumns(); + } + catch (IOException e) + { + errors.reject(SpringActionController.ERROR_MSG, e.getMessage()); + return false; + } + + for (ColumnDescriptor c : columns) + { + if (columnAliases.containsKey(c.name)) + { + labels.put(columnAliases.get(c.name), c.name); + c.name = columnAliases.get(c.name); + } + else + labels.put(c.name, c.name); + } + importer.fixupSpecimenColumns(loader); + List> specimenRows; + try + { + loader.setThrowOnErrors(true); + specimenRows = loader.load(); + } + catch (ConversionException x) + { + errors.reject(SpringActionController.ERROR_MSG, x.getMessage() + " NOTE: Numbers must contain only digits and decimal separators."); + return false; + } + + Set participants = new HashSet<>(); + Set vialIds = new HashSet<>(); + Map> sampleIdMap = new HashMap<>(); + String visitKey = study.getTimepointType() == TimepointType.VISIT ? SimpleSpecimenImporter.VISIT : SimpleSpecimenImporter.DRAW_TIMESTAMP; + + if (specimenRows.size() == 0) + errors.reject(SpringActionController.ERROR_MSG, "No specimen data was provided."); + int rowNum = 1; + for (Map row : specimenRows) + { + Object vialId = row.get(SimpleSpecimenImporter.VIAL_ID); + + String participant = (String) row.get(SimpleSpecimenImporter.PARTICIPANT_ID); + if (null == participant && null != vialId) + { + participant = (String) vialId; + row.put(SimpleSpecimenImporter.PARTICIPANT_ID, participant); + } + if (null == participant) + { + errors.reject(SpringActionController.ERROR_MSG, "Error in row " + rowNum + ": required field " + (null == labels.get(SimpleSpecimenImporter.PARTICIPANT_ID) ? + StudyService.get().getSubjectNounSingular(container) : + labels.get(SimpleSpecimenImporter.PARTICIPANT_ID)) + " was not supplied"); + } + else + participants.add(participant); + + Object sampleId = row.get(SimpleSpecimenImporter.SAMPLE_ID); + if (null == sampleId && null != vialId) + { + sampleId = row.get(SimpleSpecimenImporter.VIAL_ID); + row.put(SimpleSpecimenImporter.SAMPLE_ID, sampleId); + } + if (null != sampleId) + { + Pair participantVisit = new Pair(participant, row.get(visitKey)); + if (sampleIdMap.containsKey(sampleId)) + { + if (!participantVisit.equals(sampleIdMap.get(sampleId))) + errors.reject(SpringActionController.ERROR_MSG, "Error in row " + rowNum + ": the same sample id has multiple " + + StudyService.get().getSubjectNounSingular(container) + "/visits."); + } + else + sampleIdMap.put(sampleId, participantVisit); + } + + if (null == vialId) + { + if (sampleId != null) + { + vialId = sampleId; + } + else + { + errors.reject(SpringActionController.ERROR_MSG, "Error in row " + rowNum + ": missing " + (null == labels.get(SimpleSpecimenImporter.VIAL_ID) ? SimpleSpecimenImporter.VIAL_ID : labels.get(SimpleSpecimenImporter.VIAL_ID))); + } + } + if (!vialIds.add(vialId)) + errors.reject(SpringActionController.ERROR_MSG, "Error in row " + rowNum + ": duplicate vial id " + vialId); + + // Check timepoint field + if (study.getTimepointType() == TimepointType.CONTINUOUS && null == row.get(SimpleSpecimenImporter.DRAW_TIMESTAMP) && null == row.get(SimpleSpecimenImporter.VISIT)) + { + errors.reject(SpringActionController.ERROR_MSG, "Error: row " + rowNum + " must contain a value for field " + + (null == labels.get(SimpleSpecimenImporter.DRAW_TIMESTAMP) ? SimpleSpecimenImporter.DRAW_TIMESTAMP : labels.get(SimpleSpecimenImporter.DRAW_TIMESTAMP)) + + " or " + + (null == labels.get(SimpleSpecimenImporter.VISIT) ? SimpleSpecimenImporter.VISIT : labels.get(SimpleSpecimenImporter.VISIT))); + } + else if (study.getTimepointType() == TimepointType.DATE && null == row.get(SimpleSpecimenImporter.DRAW_TIMESTAMP)) + { + errors.reject(SpringActionController.ERROR_MSG, "Error: row " + rowNum + " does not contain a value for field " + + (null == labels.get(SimpleSpecimenImporter.DRAW_TIMESTAMP) ? SimpleSpecimenImporter.DRAW_TIMESTAMP : labels.get(SimpleSpecimenImporter.DRAW_TIMESTAMP))); + } + else if (study.getTimepointType() == TimepointType.VISIT && null == row.get(SimpleSpecimenImporter.VISIT)) + { + errors.reject(SpringActionController.ERROR_MSG, "Error: row " + rowNum + " does not contain a value for field " + + (null == labels.get(SimpleSpecimenImporter.VISIT) ? SimpleSpecimenImporter.VISIT : labels.get(SimpleSpecimenImporter.VISIT))); + } + + if (errors.getAllErrors().size() >= 3) + break; + + rowNum++; + } + + try + { + if (!errors.hasErrors()) + importer.process(specimenRows, form.isMerge()); + } + catch (IllegalStateException e) + { + errors.reject(SpringActionController.ERROR_MSG, e.getMessage()); + } + catch (RuntimeSQLException e) + { + String message = e.getMessage(); + if (e.getSQLException() instanceof BatchUpdateException && null != e.getSQLException().getNextException()) + message = e.getSQLException().getNextException().getMessage(); + errors.reject(SpringActionController.ERROR_MSG, "A database error was reported during import: " + message); + } + catch (RuntimeException e) + { + errors.reject(SpringActionController.ERROR_MSG, e.getMessage()); + } + return !errors.hasErrors(); + } + + @Override + public ActionURL getSuccessURL(UploadSpecimensForm form) + { + + String redir = form.getRedir(); + if (null != StringUtils.trimToNull(redir)) + return new ActionURL(redir); + else + return new ActionURL(ImportCompleteAction.class, getContainer()); + } + + @Override + public void addNavTrail(NavTree root) + { + Container c = getContainer(); + Study s = StudyService.get().getStudy(getContainer()); + + root.addChild(s.getLabel(), PageFlowUtil.urlProvider(StudyUrls.class).getStudyOverviewURL(getContainer())); + root.addChild("Specimen Overview", new ActionURL(SpecimenController2.OverviewAction.class, getContainer())); + root.addChild("Upload Specimens"); + } + + @RequiresPermission(AdminPermission.class) + public static class ImportCompleteAction extends SimpleViewAction + { + @Override + public ModelAndView getView(Object o, BindException errors) + { + ActionURL homeLink = PageFlowUtil.urlProvider(ProjectUrls.class).getStartURL(getContainer()); + ActionURL samplesLink = PageFlowUtil.urlProvider(SpecimenUrls.class).getSpecimensURL(getContainer(), true); + return new HtmlView("Specimens uploaded successfully.

" + + PageFlowUtil.textLink("study home", homeLink) + " " + + PageFlowUtil.textLink("specimens", samplesLink)); + } + + @Override + public void addNavTrail(NavTree root) + { + Study study = StudyService.get().getStudy(getContainer()); + + root.addChild(study.getLabel(), PageFlowUtil.urlProvider(StudyUrls.class).getStudyOverviewURL(getContainer())); + root.addChild("Specimen Overview", new ActionURL(SpecimenController2.OverviewAction.class, getContainer())); + root.addChild("Sample Import Complete"); + } + } + + public static class UploadSpecimensForm + { + private String tsv; + private String redir; + private String replaceOrMerge = "merge"; + private boolean noSpecimens = false; + + public String getTsv() + { + return tsv; + } + + public void setTsv(String tsv) + { + this.tsv = tsv; + } + + public String getRedir() + { + return redir; + } + + public void setRedir(String redir) + { + this.redir = redir; + } + + public String getReplaceOrMerge() + { + return replaceOrMerge; + } + + public void setReplaceOrMerge(String replaceOrMerge) + { + this.replaceOrMerge = replaceOrMerge; + } + + public boolean isMerge() + { + return "merge".equals(this.replaceOrMerge); + } + + public boolean isNoSpecimens() + { + return noSpecimens; + } + + public void setNoSpecimens(boolean noSpecimens) + { + this.noSpecimens = noSpecimens; + } + } +} diff --git a/specimen/src/org/labkey/specimen/action/SpecimenApiController.java b/specimen/src/org/labkey/specimen/actions/SpecimenApiController.java similarity index 94% rename from specimen/src/org/labkey/specimen/action/SpecimenApiController.java rename to specimen/src/org/labkey/specimen/actions/SpecimenApiController.java index b4265cc9..3804eb83 100644 --- a/specimen/src/org/labkey/specimen/action/SpecimenApiController.java +++ b/specimen/src/org/labkey/specimen/actions/SpecimenApiController.java @@ -13,13 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.labkey.specimen.action; +package org.labkey.specimen.actions; import org.apache.commons.lang3.StringUtils; import org.json.JSONObject; import org.labkey.api.action.ApiResponse; import org.labkey.api.action.ApiSimpleResponse; import org.labkey.api.action.ApiVersion; +import org.labkey.api.action.HasViewContext; import org.labkey.api.action.MutatingApiAction; import org.labkey.api.action.ReadOnlyApiAction; import org.labkey.api.action.SpringActionController; @@ -35,9 +36,7 @@ import org.labkey.api.specimen.SpecimenRequestManager; import org.labkey.api.specimen.SpecimenRequestStatus; import org.labkey.api.specimen.Vial; -import org.labkey.api.specimen.actions.RequestIdForm; -import org.labkey.api.specimen.actions.SpecimenApiForm; -import org.labkey.api.specimen.actions.VialRequestForm; +import org.labkey.api.specimen.actions.IdTypes; import org.labkey.api.specimen.importer.RequestabilityManager; import org.labkey.api.specimen.location.LocationImpl; import org.labkey.api.specimen.location.LocationManager; @@ -81,6 +80,23 @@ public SpecimenApiController() setActionResolver(_resolver); } + public static class SpecimenApiForm implements HasViewContext + { + private ViewContext _viewContext; + + @Override + public ViewContext getViewContext() + { + return _viewContext; + } + + @Override + public void setViewContext(ViewContext viewContext) + { + _viewContext = viewContext; + } + } + public static class GetRequestsForm extends SpecimenApiForm { Boolean _allUsers; @@ -249,6 +265,21 @@ public ApiResponse execute(GetRequestsForm requestsForm, BindException errors) } } + public static class RequestIdForm extends SpecimenApiForm + { + private int _requestId; + + public int getRequestId() + { + return _requestId; + } + + public void setRequestId(int requestId) + { + _requestId = requestId; + } + } + @RequiresPermission(ReadPermission.class) @ApiVersion(9.1) public class GetRequestAction extends ReadOnlyApiAction @@ -392,6 +423,32 @@ private SpecimenRequest getRequest(User user, Container container, int rowId, bo return request; } + public static class VialRequestForm extends RequestIdForm + { + private String _idType; + private String[] _vialIds; + + public String[] getVialIds() + { + return _vialIds; + } + + public void setVialIds(String[] vialIds) + { + _vialIds = vialIds; + } + + public String getIdType() + { + return _idType; + } + + public void setIdType(String idType) + { + _idType = idType; + } + } + @RequiresPermission(RequestSpecimensPermission.class) @ApiVersion(9.1) public class AddVialsToRequestAction extends MutatingApiAction @@ -428,9 +485,9 @@ public ApiResponse execute(VialRequestForm vialRequestForm, BindException errors private Vial getVial(String vialId, String idType) { Vial vial; - if (VialRequestForm.IdTypes.GlobalUniqueId.name().equals(idType)) + if (IdTypes.GlobalUniqueId.name().equals(idType)) vial = SpecimenManagerNew.get().getVial(getContainer(), getUser(), vialId); - else if (VialRequestForm.IdTypes.RowId.name().equals(idType)) + else if (IdTypes.RowId.name().equals(idType)) { try { @@ -445,7 +502,7 @@ else if (VialRequestForm.IdTypes.RowId.name().equals(idType)) else { throw new RuntimeException("Invalid ID type \"" + idType + "\": only \"" + - VialRequestForm.IdTypes.GlobalUniqueId.name() + "\" and \"" + VialRequestForm.IdTypes.RowId.name() + + IdTypes.GlobalUniqueId.name() + "\" and \"" + IdTypes.RowId.name() + "\" are valid parameter values."); } if (vial == null) diff --git a/specimen/src/org/labkey/specimen/actions/SpecimenController2.java b/specimen/src/org/labkey/specimen/actions/SpecimenController2.java new file mode 100644 index 00000000..7bd53f77 --- /dev/null +++ b/specimen/src/org/labkey/specimen/actions/SpecimenController2.java @@ -0,0 +1,1414 @@ +package org.labkey.specimen.actions; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.json.JSONObject; +import org.labkey.api.action.ApiResponse; +import org.labkey.api.action.ApiSimpleResponse; +import org.labkey.api.action.FormHandlerAction; +import org.labkey.api.action.FormViewAction; +import org.labkey.api.action.HasViewContext; +import org.labkey.api.action.MutatingApiAction; +import org.labkey.api.action.ReadOnlyApiAction; +import org.labkey.api.action.SimpleViewAction; +import org.labkey.api.action.SpringActionController; +import org.labkey.api.attachments.AttachmentParent; +import org.labkey.api.attachments.BaseDownloadAction; +import org.labkey.api.data.ActionButton; +import org.labkey.api.data.BeanViewForm; +import org.labkey.api.data.Container; +import org.labkey.api.data.ExcelColumn; +import org.labkey.api.data.SimpleFilter; +import org.labkey.api.module.FolderType; +import org.labkey.api.pipeline.PipeRoot; +import org.labkey.api.pipeline.PipelineService; +import org.labkey.api.pipeline.PipelineStatusUrls; +import org.labkey.api.pipeline.browse.PipelinePathForm; +import org.labkey.api.security.RequiresPermission; +import org.labkey.api.security.RequiresSiteAdmin; +import org.labkey.api.security.User; +import org.labkey.api.security.ValidEmail; +import org.labkey.api.security.permissions.AdminPermission; +import org.labkey.api.security.permissions.ReadPermission; +import org.labkey.api.specimen.SpecimenManagerNew; +import org.labkey.api.specimen.SpecimenRequestManager; +import org.labkey.api.specimen.SpecimenSearchWebPart; +import org.labkey.api.specimen.Vial; +import org.labkey.api.specimen.actions.IdForm; +import org.labkey.api.specimen.actions.ManageReqsBean; +import org.labkey.api.specimen.actions.SpecimenEventBean; +import org.labkey.api.specimen.actions.ViewRequestsHeaderBean; +import org.labkey.api.specimen.importer.RequestabilityManager; +import org.labkey.api.specimen.importer.SimpleSpecimenImporter; +import org.labkey.api.specimen.model.SpecimenRequestEvent; +import org.labkey.api.specimen.pipeline.SpecimenArchive; +import org.labkey.api.specimen.query.SpecimenEventQueryView; +import org.labkey.api.specimen.query.SpecimenRequestQueryView; +import org.labkey.api.specimen.requirements.SpecimenRequestRequirement; +import org.labkey.api.specimen.requirements.SpecimenRequestRequirementProvider; +import org.labkey.api.specimen.requirements.SpecimenRequestRequirementType; +import org.labkey.api.specimen.security.permissions.ManageDisplaySettingsPermission; +import org.labkey.api.specimen.security.permissions.ManageNotificationsPermission; +import org.labkey.api.specimen.security.permissions.ManageRequestRequirementsPermission; +import org.labkey.api.specimen.security.permissions.ManageRequestSettingsPermission; +import org.labkey.api.specimen.security.permissions.RequestSpecimensPermission; +import org.labkey.api.specimen.settings.DisplaySettings; +import org.labkey.api.specimen.settings.RepositorySettings; +import org.labkey.api.specimen.settings.RequestNotificationSettings; +import org.labkey.api.specimen.settings.SettingsManager; +import org.labkey.api.specimen.view.SpecimenWebPart; +import org.labkey.api.study.MapArrayExcelWriter; +import org.labkey.api.study.SpecimenUrls; +import org.labkey.api.study.Study; +import org.labkey.api.study.StudyService; +import org.labkey.api.study.StudyUrls; +import org.labkey.api.study.security.permissions.ManageStudyPermission; +import org.labkey.api.util.HelpTopic; +import org.labkey.api.util.PageFlowUtil; +import org.labkey.api.util.Pair; +import org.labkey.api.util.URLHelper; +import org.labkey.api.view.ActionURL; +import org.labkey.api.view.AjaxCompletion; +import org.labkey.api.view.HtmlView; +import org.labkey.api.view.JspView; +import org.labkey.api.view.NavTree; +import org.labkey.api.view.NotFoundException; +import org.labkey.api.view.RedirectException; +import org.labkey.api.view.VBox; +import org.labkey.api.view.ViewBackgroundInfo; +import org.labkey.api.view.ViewContext; +import org.labkey.api.view.WebPartView; +import org.labkey.specimen.pipeline.SpecimenBatch; +import org.springframework.validation.BindException; +import org.springframework.validation.Errors; +import org.springframework.web.servlet.ModelAndView; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +// TEMPORARY: Move specimen actions from study SpecimenController to here. Once all actions are moved, we'll rename this. +public class SpecimenController2 extends SpringActionController +{ + private static final DefaultActionResolver _resolver = new DefaultActionResolver( + SpecimenController2.class, + + ShowGroupMembersAction.class, + ShowSearchAction.class, + ShowUploadSpecimensAction.class, + ShowUploadSpecimensAction.ImportCompleteAction.class, + + // Report actions from SpecimenReportActions + SpecimenReportActions.ParticipantSummaryReportAction.class, + SpecimenReportActions.ParticipantTypeReportAction.class, + SpecimenReportActions.ParticipantSiteReportAction.class, + SpecimenReportActions.RequestReportAction.class, + SpecimenReportActions.RequestEnrollmentSiteReportAction.class, + SpecimenReportActions.RequestSiteReportAction.class, + SpecimenReportActions.RequestParticipantReportAction.class, + SpecimenReportActions.TypeParticipantReportAction.class, + SpecimenReportActions.TypeSummaryReportAction.class, + SpecimenReportActions.TypeCohortReportAction.class + ); + + private Study _study = null; + + public SpecimenController2() + { + setActionResolver(_resolver); + } + + @Nullable + public Study getStudy() + { + if (null == _study) + _study = StudyService.get().getStudy(getContainer()); + return _study; + } + + @NotNull + public Study getStudyThrowIfNull() throws IllegalStateException + { + Study study = StudyService.get().getStudy(getContainer()); + if (null == study) + { + // We expected to find a study + throw new NotFoundException("No study found."); + } + return study; + } + + @NotNull + public Study getStudyRedirectIfNull() + { + Study study = StudyService.get().getStudy(getContainer()); + if (null == study) + { + // redirect to the study home page, where admins will see a 'create study' button, + // and non-admins will simply see a message that no study exists. + throw new RedirectException(urlProvider(StudyUrls.class).getBeginURL(getContainer())); + } + return study; + } + + public void addRootNavTrail(NavTree root) + { + Study study = getStudyRedirectIfNull(); + Container c = getContainer(); + ActionURL rootURL; + FolderType folderType = c.getFolderType(); + if ("study".equals(folderType.getDefaultModule().getName())) + { + rootURL = folderType.getStartURL(c, getUser()); + } + else + { + rootURL = urlProvider(StudyUrls.class).getBeginURL(c); + } + root.addChild(study.getLabel(), rootURL); + } + + private void addBaseSpecimenNavTrail(NavTree root) + { + addRootNavTrail(root); + ActionURL overviewURL = new ActionURL(OverviewAction.class, getContainer()); + root.addChild("Specimen Overview", overviewURL); + } + + private void addSpecimenRequestsNavTrail(NavTree root) + { + addBaseSpecimenNavTrail(root); + root.addChild("Specimen Requests", new ActionURL(ViewRequestsAction.class, getContainer())); + } + + private void addManageStudyNavTrail(NavTree root) + { + urlProvider(StudyUrls.class).addManageStudyNavTrail(root, getContainer(), getUser()); + } + + private ActionURL getManageStudyURL() + { + return urlProvider(StudyUrls.class).getManageStudyURL(getContainer()); + } + + public void ensureSpecimenRequestsConfigured(boolean checkExistingStatuses) + { + if (!SettingsManager.get().isSpecimenRequestEnabled(getContainer(), checkExistingStatuses)) + throw new RedirectException(new ActionURL(SpecimenRequestConfigRequiredAction.class, getContainer())); + } + + @RequiresPermission(ReadPermission.class) + public class OverviewAction extends SimpleViewAction + { + @Override + public ModelAndView getView(Object form, BindException errors) + { + if (null == StudyService.get().getStudy(getContainer())) + return new HtmlView("This folder does not contain a study."); + SpecimenSearchWebPart specimenSearch = new SpecimenSearchWebPart(true); + SpecimenWebPart specimenSummary = new SpecimenWebPart(true, StudyService.get().getStudy(getContainer())); + return new VBox(specimenSummary, specimenSearch); + } + + @Override + public void addNavTrail(NavTree root) + { + addBaseSpecimenNavTrail(root); + } + } + + public static class SpecimenWebPartForm + { + private String[] _grouping1; + private String[] _grouping2; + private String[] _columns; + + public String[] getGrouping1() + { + return _grouping1; + } + + public void setGrouping1(String[] grouping1) + { + _grouping1 = grouping1; + } + + public String[] getGrouping2() + { + return _grouping2; + } + + public void setGrouping2(String[] grouping2) + { + _grouping2 = grouping2; + } + + public String[] getColumns() + { + return _columns; + } + + public void setColumns(String[] columns) + { + _columns = columns; + } + } + + @RequiresPermission(ManageDisplaySettingsPermission.class) + public static class ManageSpecimenWebPartAction extends SimpleViewAction + { + @Override + public ModelAndView getView(SpecimenWebPartForm form, BindException errors) + { + RepositorySettings settings = SettingsManager.get().getRepositorySettings(getContainer()); + ArrayList groupings = settings.getSpecimenWebPartGroupings(); + form.setGrouping1(groupings.get(0)); + form.setGrouping2(groupings.get(1)); + form.setColumns(SpecimenRequestManager.get().getGroupedValueAllowedColumns()); + return new JspView<>("/org/labkey/specimen/view/manageSpecimenWebPart.jsp", form); + } + + @Override + public void addNavTrail(NavTree root) + { + setHelpTopic("manageSpecimens#group"); + urlProvider(StudyUrls.class).addManageStudyNavTrail(root, getContainer(), getUser()); + root.addChild("Configure Specimen Web Part"); + } + } + + @RequiresPermission(AdminPermission.class) + public class SaveSpecimenWebPartSettingsAction extends MutatingApiAction + { + @Override + public ApiResponse execute(SpecimenWebPartForm form, BindException errors) + { + ApiSimpleResponse response = new ApiSimpleResponse(); + Study study = getStudy(); + if (study != null) + { + Container container = getContainer(); + RepositorySettings settings = SettingsManager.get().getRepositorySettings(container); + ArrayList groupings = new ArrayList<>(2); + groupings.add(form.getGrouping1()); + groupings.add(form.getGrouping2()); + settings.setSpecimenWebPartGroupings(groupings); + SettingsManager.get().saveRepositorySettings(container, settings); + response.put("success", true); + return response; + } + else + throw new IllegalStateException("A study does not exist in this folder"); + } + } + + @RequiresSiteAdmin + public static class PivotAction extends SimpleViewAction + { + @Override + public ModelAndView getView(Object o, BindException errors) + { + return new JspView<>("/org/labkey/specimen/view/pivot.jsp"); + } + + @Override + public void addNavTrail(NavTree root) + { + } + } + + public static class PipelineForm extends PipelinePathForm + { + private String replaceOrMerge = "replace"; + + public String getReplaceOrMerge() + { + return replaceOrMerge; + } + + public void setReplaceOrMerge(String replaceOrMerge) + { + this.replaceOrMerge = replaceOrMerge; + } + + public boolean isMerge() + { + return "merge".equals(this.replaceOrMerge); + } + } + + public static void submitSpecimenBatch(Container c, User user, ActionURL url, File f, PipeRoot root, boolean merge) throws IOException + { + if (null == f || !f.exists() || !f.isFile()) + throw new NotFoundException(); + + SpecimenBatch batch = new SpecimenBatch(new ViewBackgroundInfo(c, user, url), f, root, merge); + batch.submit(); + } + + @RequiresPermission(AdminPermission.class) + public class SubmitSpecimenBatchImportAction extends FormHandlerAction + { + @Override + public void validateCommand(PipelineForm target, Errors errors) + { + } + + @Override + public boolean handlePost(PipelineForm form, BindException errors) throws Exception + { + Container c = getContainer(); + PipeRoot root = PipelineService.get().findPipelineRoot(c); + boolean first = true; + for (File f : form.getValidatedFiles(c)) + { + // Only possibly overwrite when the first archive is loaded: + boolean merge = !first || form.isMerge(); + submitSpecimenBatch(c, getUser(), getViewContext().getActionURL(), f, root, merge); + first = false; + } + return true; + } + + @Override + public ActionURL getSuccessURL(PipelineForm pipelineForm) + { + return urlProvider(PipelineStatusUrls.class).urlBegin(getContainer()); + } + } + + /** + * Legacy method hit via WGET/CURL to programmatically initiate a specimen import; no longer used by the UI, + * but this method should be kept around until we receive verification that the URL is no longer being hit + * programmatically. + */ + @RequiresPermission(AdminPermission.class) + public class SubmitSpecimenImport extends FormHandlerAction + { + @Override + public void validateCommand(PipelineForm target, Errors errors) + { + } + + @Override + public boolean handlePost(PipelineForm form, BindException errors) throws Exception + { + Container c = getContainer(); + String path = form.getPath(); + File f = null; + + PipeRoot root = PipelineService.get().findPipelineRoot(c); + if (path != null) + { + if (root != null) + f = root.resolvePath(path); + } + + submitSpecimenBatch(c, getUser(), getViewContext().getActionURL(), f, root, form.isMerge()); + return true; + } + + @Override + public ActionURL getSuccessURL(PipelineForm pipelineForm) + { + return urlProvider(PipelineStatusUrls.class).urlBegin(getContainer()); + } + } + + + public static class ImportSpecimensBean + { + private final String _path; + private final List _archives; + private final List _errors; + private final Container _container; + private final String[] _files; + + private boolean noSpecimens = false; + private boolean _defaultMerge = false; + private boolean _isEditableSpecimens = false; + + public ImportSpecimensBean(Container container, List archives, + String path, String[] files, List errors) + { + _path = path; + _files = files; + _archives = archives; + _errors = errors; + _container = container; + } + + public List getArchives() + { + return _archives; + } + + public String getPath() + { + return _path; + } + + public String[] getFiles() + { + return _files; + } + + public List getErrors() + { + return _errors; + } + + public Container getContainer() + { + return _container; + } + + public boolean isNoSpecimens() + { + return noSpecimens; + } + + public void setNoSpecimens(boolean noSpecimens) + { + this.noSpecimens = noSpecimens; + } + + public boolean isDefaultMerge() + { + return _defaultMerge; + } + + public void setDefaultMerge(boolean defaultMerge) + { + _defaultMerge = defaultMerge; + } + + public boolean isEditableSpecimens() + { + return _isEditableSpecimens; + } + + public void setEditableSpecimens(boolean editableSpecimens) + { + _isEditableSpecimens = editableSpecimens; + } + } + + @RequiresPermission(AdminPermission.class) + public class ImportSpecimenDataAction extends SimpleViewAction + { + private String[] _filePaths = null; + + @Override + public ModelAndView getView(PipelineForm form, BindException bindErrors) + { + List dataFiles = form.getValidatedFiles(getContainer()); + List archives = new ArrayList<>(); + List errors = new ArrayList<>(); + _filePaths = form.getFile(); + for (File dataFile : dataFiles) + { + if (null == dataFile || !dataFile.exists() || !dataFile.isFile()) + { + throw new NotFoundException(); + } + + if (!dataFile.canRead()) + errors.add("Can't read data file: " + dataFile); + + SpecimenArchive archive = new SpecimenArchive(dataFile); + archives.add(archive); + } + + ImportSpecimensBean bean = new ImportSpecimensBean(getContainer(), archives, form.getPath(), form.getFile(), errors); + boolean isEmpty = SpecimenManagerNew.get().isSpecimensEmpty(getContainer(), getUser()); + if (isEmpty) + { + bean.setNoSpecimens(true); + } + else if (SettingsManager.get().getRepositorySettings(getStudyThrowIfNull().getContainer()).isSpecimenDataEditable()) + { + bean.setDefaultMerge(true); // Repository is editable; make Merge the default + bean.setEditableSpecimens(true); + } + + return new JspView<>("/org/labkey/specimen/view/importSpecimens.jsp", bean); + } + + @Override + public void addNavTrail(NavTree root) + { + String msg; + if (_filePaths.length == 1) + msg = _filePaths[0]; + else + msg = _filePaths.length + " specimen archives"; + root.addChild("Import Study Batch - " + msg); + } + } + + @RequiresPermission(AdminPermission.class) + public class GetSpecimenExcelAction extends SimpleViewAction + { + @Override + public ModelAndView getView(Object o, BindException errors) + { + List> defaultSpecimens = new ArrayList<>(); + SimpleSpecimenImporter importer = new SimpleSpecimenImporter(getContainer(), getUser(), + getStudyRedirectIfNull().getTimepointType(), StudyService.get().getSubjectNounSingular(getContainer())); + MapArrayExcelWriter xlWriter = new MapArrayExcelWriter(defaultSpecimens, importer.getSimpleSpecimenColumns()); + for (ExcelColumn col : xlWriter.getColumns()) + col.setCaption(importer.label(col.getName())); + + xlWriter.write(getViewContext().getResponse()); + + return null; + } + + @Override + public void addNavTrail(NavTree root) + { + } + } + + static class SpecimenEventForm + { + private String _id; + private Container _targetStudy; + + public String getId() + { + return _id; + } + + public void setId(String id) + { + _id = id; + } + + public Container getTargetStudy() + { + return _targetStudy; + } + + public void setTargetStudy(Container targetStudy) + { + _targetStudy = targetStudy; + } + } + + @SuppressWarnings("unused") // Referenced in SpecimenForeignKey + @RequiresPermission(ReadPermission.class) + public static class SpecimenEventsRedirectAction extends SimpleViewAction + { + @Override + public ModelAndView getView(SpecimenEventForm form, BindException errors) + { + if (form.getId() != null && form.getTargetStudy() != null) + { + Vial vial = SpecimenManagerNew.get().getVial(form.getTargetStudy(), getUser(), form.getId()); + if (vial != null) + { + ActionURL url = new ActionURL(SpecimenEventsAction.class, form.getTargetStudy()).addParameter("id", vial.getRowId()); + throw new RedirectException(url); + } + } + return new HtmlView("Unable to resolve the Specimen ID and target Study"); + } + + @Override + public void addNavTrail(NavTree root) + { + } + } + + public static class SpecimenEventAttachmentForm + { + private int _eventId; + private String _name; + + public int getEventId() + { + return _eventId; + } + + public void setEventId(int eventId) + { + _eventId = eventId; + } + + public String getName() + { + return _name; + } + + public void setName(String name) + { + _name = name; + } + } + + public static ActionURL getDownloadURL(SpecimenRequestEvent event, String name) + { + return new ActionURL(DownloadAction.class, event.getContainer()) + .addParameter("eventId", event.getRowId()) + .addParameter("name", name); + } + + @RequiresPermission(ReadPermission.class) + public static class DownloadAction extends BaseDownloadAction + { + @Override + public @Nullable Pair getAttachment(SpecimenEventAttachmentForm form) + { + SpecimenRequestEvent event = SpecimenRequestManager.get().getRequestEvent(getContainer(), form.getEventId()); + if (event == null) + throw new NotFoundException("Specimen event not found"); + + return new Pair<>(event, form.getName()); + } + } + + @RequiresPermission(ReadPermission.class) + public class AutoReportListAction extends SimpleViewAction + { + @Override + public ModelAndView getView(Object form, BindException errors) + { + return new JspView<>("/org/labkey/specimen/view/autoReportList.jsp", new ReportConfigurationBean(getViewContext())); + } + + @Override + public void addNavTrail(NavTree root) + { + setHelpTopic("exploreSpecimens"); + addBaseSpecimenNavTrail(root); + root.addChild("Specimen Reports"); + } + } + + @RequiresPermission(ReadPermission.class) + public class SpecimenRequestConfigRequiredAction extends SimpleViewAction + { + @Override + public ModelAndView getView(Object o, BindException errors) + { + return new JspView<>("/org/labkey/specimen/view/configurationRequired.jsp"); + } + + @Override + public void addNavTrail(NavTree root) + { + addRootNavTrail(root); + root.addChild("Unable to Request Specimens"); + } + } + + @RequiresPermission(ManageRequestSettingsPermission.class) + public class ConfigureRequestabilityRulesAction extends SimpleViewAction + { + @Override + public ModelAndView getView(Object o, BindException errors) + { + ensureSpecimenRequestsConfigured(false); + return new JspView<>("/org/labkey/specimen/view/configRequestabilityRules.jsp"); + } + + @Override + public void addNavTrail(NavTree root) + { + setHelpTopic("coordinateSpecimens#requestability"); + addManageStudyNavTrail(root); + root.addChild("Configure Requestability Rules"); + } + } + + @RequiresPermission(ReadPermission.class) + public class ViewRequestsAction extends SimpleViewAction + { + @Override + public ModelAndView getView(Object o, BindException errors) + { + requiresLogin(); + SpecimenRequestQueryView grid = SpecimenRequestQueryView.createView(getViewContext()); + grid.setExtraLinks(true); + grid.setShowCustomizeLink(false); + grid.setShowDetailsColumn(false); + if (getContainer().hasPermission(getUser(), RequestSpecimensPermission.class)) + { + ActionButton insertButton = new ActionButton(urlProvider(SpecimenUrls.class).getShowCreateSpecimenRequestAction(getContainer()), "Create New Request", ActionButton.Action.LINK); + grid.setButtons(Collections.singletonList(insertButton)); + } + else + { + grid.setButtons(Collections.emptyList()); + } + + JspView header = new JspView<>("/org/labkey/specimen/view/viewRequestsHeader.jsp", + new ViewRequestsHeaderBean(getViewContext(), grid)); + + return new VBox(header, grid); + } + + @Override + public void addNavTrail(NavTree root) + { + addSpecimenRequestsNavTrail(root); + } + } + + public static class ViewEventForm extends IdForm + { + private boolean _selected; + private boolean _vialView; + + public boolean isSelected() + { + return _selected; + } + + public void setSelected(boolean selected) + { + _selected = selected; + } + + public boolean isVialView() + { + return _vialView; + } + + public void setVialView(boolean vialView) + { + _vialView = vialView; + } + } + + @RequiresPermission(ReadPermission.class) + public class SpecimenEventsAction extends SimpleViewAction + { + private boolean _showingSelectedSpecimens; + + @Override + public ModelAndView getView(ViewEventForm viewEventForm, BindException errors) + { + Study study = getStudyThrowIfNull(); + _showingSelectedSpecimens = viewEventForm.isSelected(); + Vial vial = SpecimenManagerNew.get().getVial(getContainer(), getUser(), viewEventForm.getId()); + if (vial == null) + throw new NotFoundException("Specimen " + viewEventForm.getId() + " does not exist."); + + JspView summaryView = new JspView<>("/org/labkey/study/view/specimen/specimen.jsp", + new SpecimenEventBean(vial, viewEventForm.getReturnUrl())); + summaryView.setTitle("Vial Summary"); + + SpecimenEventQueryView vialHistoryView = SpecimenEventQueryView.createView(getViewContext(), vial); + vialHistoryView.setTitle("Vial History"); + + VBox vbox; + + if (SettingsManager.get().getRepositorySettings(getStudyRedirectIfNull().getContainer()).isEnableRequests()) + { + List requestIds = SpecimenRequestManager.get().getRequestIdsForSpecimen(vial); + SimpleFilter requestFilter; + WebPartView relevantRequests; + + if (!requestIds.isEmpty()) + { + requestFilter = new SimpleFilter(); + StringBuilder whereClause = new StringBuilder(); + for (int i = 0; i < requestIds.size(); i++) + { + if (i > 0) + whereClause.append(" OR "); + whereClause.append("RequestId = ?"); + } + requestFilter.addWhereClause(whereClause.toString(), requestIds.toArray()); + SpecimenRequestQueryView queryView = SpecimenRequestQueryView.createView(getViewContext(), requestFilter); + queryView.setExtraLinks(true); + relevantRequests = queryView; + } + else + relevantRequests = new JspView("/org/labkey/specimen/view/relevantRequests.jsp"); + relevantRequests.setTitle("Relevant Vial Requests"); + vbox = new VBox(summaryView, vialHistoryView, relevantRequests); + } + else + { + vbox = new VBox(summaryView, vialHistoryView); + } + + return vbox; + } + + @Override + public void addNavTrail(NavTree root) + { + addBaseSpecimenNavTrail(root); + if (_showingSelectedSpecimens) + { + root.addChild("Selected Specimens", urlProvider(SpecimenUrls.class).getSpecimensURL(getContainer(), true)); + } + root.addChild("Vial History"); + } + } + + private boolean isNullOrBlank(String toCheck) + { + return ((toCheck == null) || toCheck.equals("")); + } + + @RequiresPermission(ManageNotificationsPermission.class) + public class ManageNotificationsAction extends FormViewAction + { + @Override + public void validateCommand(RequestNotificationSettings form, Errors errors) + { + String replyTo = form.getReplyTo(); + if (replyTo == null || replyTo.length() == 0) + { + errors.reject(SpringActionController.ERROR_MSG, "Reply-to cannot be empty."); + } + else if (!RequestNotificationSettings.REPLY_TO_CURRENT_USER_VALUE.equals(replyTo)) + { + try + { + new ValidEmail(replyTo); + } + catch(ValidEmail.InvalidEmailException e) + { + errors.reject(SpringActionController.ERROR_MSG, replyTo + " is not a valid email address."); + } + } + + String subjectSuffix = form.getSubjectSuffix(); + if (subjectSuffix == null || subjectSuffix.length() == 0) + { + errors.reject(SpringActionController.ERROR_MSG, "Subject suffix cannot be empty."); + } + + try + { + form.getNewRequestNotifyAddresses(); + } + catch (ValidEmail.InvalidEmailException e) + { + errors.reject(SpringActionController.ERROR_MSG, e.getBadEmail() + " is not a valid email address."); + } + + try + { + form.getCCAddresses(); + } + catch (ValidEmail.InvalidEmailException e) + { + errors.reject(SpringActionController.ERROR_MSG, e.getBadEmail() + " is not a valid email address."); + } + } + + @Override + public ModelAndView getView(RequestNotificationSettings form, boolean reshow, BindException errors) + { + ensureSpecimenRequestsConfigured(false); + + // try to get the settings from the form, just in case this is a reshow: + RequestNotificationSettings settings = form; + if (settings == null || settings.getReplyTo() == null) + settings = SettingsManager.get().getRequestNotificationSettings(getContainer()); + + return new JspView<>("/org/labkey/specimen/view/manageNotifications.jsp", settings, errors); + } + + @Override + public boolean handlePost(RequestNotificationSettings settings, BindException errors) + { + ensureSpecimenRequestsConfigured(false); + + if (!settings.isNewRequestNotifyCheckbox()) + settings.setNewRequestNotify(null); + else + { + if (isNullOrBlank(settings.getNewRequestNotify())) + errors.reject(ERROR_MSG, "New request notify is blank and send email is checked"); + } + if (!settings.isCcCheckbox()) + settings.setCc(null); + else + { + if (isNullOrBlank(settings.getCc())) + errors.reject(ERROR_MSG, "Always CC is blank and send email is checked"); + } + if (errors.hasErrors()) + return false; + + SettingsManager.get().saveRequestNotificationSettings(getContainer(), settings); + return true; + } + + @Override + public ActionURL getSuccessURL(RequestNotificationSettings form) + { + return getManageStudyURL(); + } + + @Override + public void addNavTrail(NavTree root) + { + setHelpTopic("coordinateSpecimens#notify"); + addManageStudyNavTrail(root); + root.addChild("Manage Notifications"); + } + } + + @RequiresPermission(ManageDisplaySettingsPermission.class) + public class ManageDisplaySettingsAction extends FormViewAction + { + @Override + public void validateCommand(DisplaySettingsForm target, Errors errors) + { + } + + @Override + public ModelAndView getView(DisplaySettingsForm form, boolean reshow, BindException errors) + { + // try to get the settings from the form, just in case this is a reshow: + DisplaySettings settings = form.getBean(); + if (settings == null || settings.getLastVialEnum() == null) + settings = SettingsManager.get().getDisplaySettings(getContainer()); + + return new JspView<>("/org/labkey/specimen/view/manageDisplay.jsp", settings); + } + + @Override + public boolean handlePost(DisplaySettingsForm form, BindException errors) + { + DisplaySettings settings = form.getBean(); + SettingsManager.get().saveDisplaySettings(getContainer(), settings); + + return true; + } + + @Override + public ActionURL getSuccessURL(DisplaySettingsForm displaySettingsForm) + { + return getManageStudyURL(); + } + + @Override + public void addNavTrail(NavTree root) + { + setHelpTopic(new HelpTopic("specimenRequest#display")); + addManageStudyNavTrail(root); + root.addChild("Manage Specimen Display Settings"); + } + } + + public static class DisplaySettingsForm extends BeanViewForm + { + public DisplaySettingsForm() + { + super(DisplaySettings.class); + } + } + + public static class UpdateRequestabilityRulesForm implements HasViewContext + { + private ViewContext _viewContext; + private String[] _ruleType; + private String[] _ruleData; + private String[] _markType; + + @Override + public ViewContext getViewContext() + { + return _viewContext; + } + + @Override + public void setViewContext(ViewContext viewContext) + { + _viewContext = viewContext; + } + + public String[] getRuleType() + { + return _ruleType; + } + + @SuppressWarnings("unused") + public void setRuleType(String[] ruleType) + { + _ruleType = ruleType; + } + + public String[] getRuleData() + { + return _ruleData; + } + + @SuppressWarnings("unused") + public void setRuleData(String[] ruleData) + { + _ruleData = ruleData; + } + + public String[] getMarkType() + { + return _markType; + } + + public void setMarkType(String[] markType) + { + _markType = markType; + } + } + + @RequiresPermission(ManageRequestSettingsPermission.class) + public static class UpdateRequestabilityRulesAction extends MutatingApiAction + { + @Override + public ApiResponse execute(UpdateRequestabilityRulesForm form, BindException errors) + { + final List rules = new ArrayList<>(); + for (int i = 0; i < form.getRuleType().length; i++) + { + String typeName = form.getRuleType()[i]; + RequestabilityManager.RuleType type = RequestabilityManager.RuleType.valueOf(typeName); + String dataString = form.getRuleData()[i]; + rules.add(type.createRule(getContainer(), dataString)); + } + RequestabilityManager.getInstance().saveRules(getContainer(), getUser(), rules); + + return new ApiSimpleResponse(Collections.singletonMap("savedCount", rules.size())); + } + } + + @RequiresPermission(ManageStudyPermission.class) + public class ManageRepositorySettingsAction extends FormViewAction + { + @Override + public ModelAndView getView(ManageRepositorySettingsForm from, boolean reshow, BindException errors) + { + return new JspView<>("/org/labkey/specimen/view/manageRepositorySettings.jsp", SettingsManager.get().getRepositorySettings(getContainer())); + } + + @Override + public void validateCommand(ManageRepositorySettingsForm form, Errors errors) + { + } + + @Override + public boolean handlePost(ManageRepositorySettingsForm form, BindException errors) throws Exception + { + RepositorySettings settings = SettingsManager.get().getRepositorySettings(getContainer()); + settings.setSimple(form.isSimple()); + settings.setEnableRequests(!form.isSimple() && form.isEnableRequests()); + settings.setSpecimenDataEditable(!form.isSimple() && form.isSpecimenDataEditable()); + SettingsManager.get().saveRepositorySettings(getContainer(), settings); + + return true; + } + + @Override + public URLHelper getSuccessURL(ManageRepositorySettingsForm manageRepositorySettingsForm) + { + return getManageStudyURL(); + } + + @Override + public void addNavTrail(NavTree root) + { + setHelpTopic("specimenAdminTutorial"); + addManageStudyNavTrail(root); + root.addChild("Manage Repository Settings"); + } + } + + public static class ManageRepositorySettingsForm + { + private boolean _simple; + private boolean _enableRequests; + private boolean _specimenDataEditable; + + public boolean isSimple() + { + return _simple; + } + + public void setSimple(boolean simple) + { + _simple = simple; + } + + public boolean isEnableRequests() + { + return _enableRequests; + } + + public void setEnableRequests(boolean enableRequests) + { + _enableRequests = enableRequests; + } + + public boolean isSpecimenDataEditable() + { + return _specimenDataEditable; + } + + public void setSpecimenDataEditable(boolean specimenDataEditable) + { + _specimenDataEditable = specimenDataEditable; + } + } + + public static class DefaultRequirementsForm + { + private int _originatorActor; + private String _originatorDescription; + private int _providerActor; + private String _providerDescription; + private int _receiverActor; + private String _receiverDescription; + private int _generalActor; + private String _generalDescription; + private String _nextPage; + + public int getGeneralActor() + { + return _generalActor; + } + + @SuppressWarnings("unused") + public void setGeneralActor(int generalActor) + { + _generalActor = generalActor; + } + + public String getGeneralDescription() + { + return _generalDescription; + } + + @SuppressWarnings("unused") + public void setGeneralDescription(String generalDescription) + { + _generalDescription = generalDescription; + } + + public int getProviderActor() + { + return _providerActor; + } + + @SuppressWarnings("unused") + public void setProviderActor(int providerActor) + { + _providerActor = providerActor; + } + + public String getProviderDescription() + { + return _providerDescription; + } + + @SuppressWarnings("unused") + public void setProviderDescription(String providerDescription) + { + _providerDescription = providerDescription; + } + + public int getReceiverActor() + { + return _receiverActor; + } + + @SuppressWarnings("unused") + public void setReceiverActor(int receiverActor) + { + _receiverActor = receiverActor; + } + + public String getReceiverDescription() + { + return _receiverDescription; + } + + @SuppressWarnings("unused") + public void setReceiverDescription(String receiverDescription) + { + _receiverDescription = receiverDescription; + } + + public int getOriginatorActor() + { + return _originatorActor; + } + + @SuppressWarnings("unused") + public void setOriginatorActor(int originatorActor) + { + _originatorActor = originatorActor; + } + + public String getOriginatorDescription() + { + return _originatorDescription; + } + + @SuppressWarnings("unused") + public void setOriginatorDescription(String originatorDescription) + { + _originatorDescription = originatorDescription; + } + + public String getNextPage() + { + return _nextPage; + } + + @SuppressWarnings("unused") + public void setNextPage(String nextPage) + { + _nextPage = nextPage; + } + } + + @RequiresPermission(ManageRequestRequirementsPermission.class) + public class ManageDefaultReqsAction extends FormViewAction + { + @Override + public void validateCommand(DefaultRequirementsForm target, Errors errors) + { + } + + @Override + public ModelAndView getView(DefaultRequirementsForm defaultRequirementsForm, boolean reshow, BindException errors) + { + ensureSpecimenRequestsConfigured(true); + return new JspView<>("/org/labkey/specimen/view/manageDefaultReqs.jsp", + new ManageReqsBean(getUser(), getContainer())); + } + + @Override + public boolean handlePost(DefaultRequirementsForm form, BindException errors) + { + ensureSpecimenRequestsConfigured(true); + createDefaultRequirement(form.getOriginatorActor(), form.getOriginatorDescription(), SpecimenRequestRequirementType.ORIGINATING_SITE); + createDefaultRequirement(form.getProviderActor(), form.getProviderDescription(), SpecimenRequestRequirementType.PROVIDING_SITE); + createDefaultRequirement(form.getReceiverActor(), form.getReceiverDescription(), SpecimenRequestRequirementType.RECEIVING_SITE); + createDefaultRequirement(form.getGeneralActor(), form.getGeneralDescription(), SpecimenRequestRequirementType.NON_SITE_BASED); + return true; + } + + private void createDefaultRequirement(Integer actorId, String description, SpecimenRequestRequirementType type) + { + if (actorId != null && actorId.intValue() > 0 && description != null && description.length() > 0) + { + SpecimenRequestRequirement requirement = new SpecimenRequestRequirement(); + requirement.setContainer(getContainer()); + requirement.setActorId(actorId); + requirement.setDescription(description); + requirement.setRequestId(-1); + SpecimenRequestRequirementProvider.get().createDefaultRequirement(getUser(), requirement, type); + } + } + + @Override + public ActionURL getSuccessURL(DefaultRequirementsForm form) + { + if (form.getNextPage() != null && form.getNextPage().length() > 0) + return new ActionURL(form.getNextPage()); + else + return getManageStudyURL(); + } + + @Override + public void addNavTrail(NavTree root) + { + setHelpTopic("coordinateSpecimens#requirements"); + addManageStudyNavTrail(root); + root.addChild("Manage Default Requirements"); + } + } + + @RequiresPermission(ManageRequestRequirementsPermission.class) + public class DeleteDefaultRequirementAction extends FormHandlerAction + { + @Override + public void validateCommand(IdForm target, Errors errors) + { + } + + @Override + public boolean handlePost(IdForm form, BindException errors) throws Exception + { + SpecimenRequestRequirement requirement = + SpecimenRequestRequirementProvider.get().getRequirement(getContainer(), form.getId()); + // we should only be deleting default requirements (those without an associated request): + if (requirement != null && requirement.getRequestId() == -1) + { + SpecimenRequestManager.get().deleteRequestRequirement(getUser(), requirement, false); + return true; + } + + return false; + } + + @Override + public ActionURL getSuccessURL(IdForm requirementForm) + { + return new ActionURL(ManageDefaultReqsAction.class, getContainer()); + } + } + + public static class CompleteSpecimenForm + { + private String _prefix; + + public String getPrefix() + { + return _prefix; + } + + public void setPrefix(String prefix) + { + _prefix = prefix; + } + } + + @RequiresPermission(AdminPermission.class) + public class CompleteSpecimenAction extends ReadOnlyApiAction + { + @Override + public ApiResponse execute(CompleteSpecimenForm form, BindException errors) + { + ApiSimpleResponse response = new ApiSimpleResponse(); + + Study study = getStudy(); + if (study == null) + throw new NotFoundException("No study exists in this folder."); + + List completions = new ArrayList<>(); + for (AjaxCompletion completion : getAjaxCompletions(study)) + completions.add(completion.toJSON()); + + response.put("completions", completions); + return response; + } + } + + private List getAjaxCompletions(Study study) + { + List completions = new ArrayList<>(); + String allString = "All " + PageFlowUtil.filter(StudyService.get().getSubjectNounPlural(study.getContainer())) + " (Large Report)"; + + completions.add(new AjaxCompletion(allString, allString)); + + for (String ptid : StudyService.get().getParticipantIds(study, getViewContext().getUser())) + completions.add(new AjaxCompletion(ptid, ptid)); + + return completions; + } +} diff --git a/specimen/src/org/labkey/specimen/actions/SpecimenReportActions.java b/specimen/src/org/labkey/specimen/actions/SpecimenReportActions.java new file mode 100644 index 00000000..05b8648d --- /dev/null +++ b/specimen/src/org/labkey/specimen/actions/SpecimenReportActions.java @@ -0,0 +1,128 @@ +package org.labkey.specimen.actions; + +import org.labkey.api.security.RequiresPermission; +import org.labkey.api.security.permissions.ReadPermission; +import org.labkey.specimen.report.SpecimenVisitReportAction; +import org.labkey.specimen.report.participant.ParticipantSiteReportFactory; +import org.labkey.specimen.report.participant.ParticipantSummaryReportFactory; +import org.labkey.specimen.report.participant.ParticipantTypeReportFactory; +import org.labkey.specimen.report.request.RequestEnrollmentSiteReportFactory; +import org.labkey.specimen.report.request.RequestLocationReportFactory; +import org.labkey.specimen.report.request.RequestParticipantReportFactory; +import org.labkey.specimen.report.request.RequestReportFactory; +import org.labkey.specimen.report.specimentype.TypeCohortReportFactory; +import org.labkey.specimen.report.specimentype.TypeParticipantReportFactory; +import org.labkey.specimen.report.specimentype.TypeSummaryReportFactory; + +public class SpecimenReportActions +{ + @RequiresPermission(ReadPermission.class) + public static class ParticipantSummaryReportAction extends SpecimenVisitReportAction + { + public ParticipantSummaryReportAction() + { + super(ParticipantSummaryReportFactory.class); + } + // no implementation needed; this action exists only to provide an entry point + // with request->bean translation for this report type. + } + + @RequiresPermission(ReadPermission.class) + public static class ParticipantTypeReportAction extends SpecimenVisitReportAction + { + public ParticipantTypeReportAction() + { + super(ParticipantTypeReportFactory.class); + } + // no implementation needed; this action exists only to provide an entry point + // with request->bean translation for this report type. + } + + @RequiresPermission(ReadPermission.class) + public static class ParticipantSiteReportAction extends SpecimenVisitReportAction + { + public ParticipantSiteReportAction() + { + super(ParticipantSiteReportFactory.class); + } + // no implementation needed; this action exists only to provide an entry point + // with request->bean translation for this report type. + } + + @RequiresPermission(ReadPermission.class) + public static class RequestReportAction extends SpecimenVisitReportAction + { + public RequestReportAction() + { + super(RequestReportFactory.class); + } + // no implementation needed; this action exists only to provide an entry point + // with request->bean translation for this report type. + } + + @RequiresPermission(ReadPermission.class) + public static class RequestEnrollmentSiteReportAction extends SpecimenVisitReportAction + { + public RequestEnrollmentSiteReportAction() + { + super(RequestEnrollmentSiteReportFactory.class); + } + // no implementation needed; this action exists only to provide an entry point + // with request->bean translation for this report type. + } + + @RequiresPermission(ReadPermission.class) + public static class RequestSiteReportAction extends SpecimenVisitReportAction + { + public RequestSiteReportAction() + { + super(RequestLocationReportFactory.class); + } + // no implementation needed; this action exists only to provide an entry point + // with request->bean translation for this report type. + } + + @RequiresPermission(ReadPermission.class) + public static class RequestParticipantReportAction extends SpecimenVisitReportAction + { + public RequestParticipantReportAction() + { + super(RequestParticipantReportFactory.class); + } + // no implementation needed; this action exists only to provide an entry point + // with request->bean translation for this report type. + } + + @RequiresPermission(ReadPermission.class) + public static class TypeParticipantReportAction extends SpecimenVisitReportAction + { + public TypeParticipantReportAction() + { + super(TypeParticipantReportFactory.class); + } + // no implementation needed; this action exists only to provide an entry point + // with request->bean translation for this report type. + } + + @RequiresPermission(ReadPermission.class) + public static class TypeSummaryReportAction extends SpecimenVisitReportAction + { + public TypeSummaryReportAction() + { + super(TypeSummaryReportFactory.class); + } + // no implementation needed; this action exists only to provide an entry point + // with request->bean translation for this report type. + } + + @RequiresPermission(ReadPermission.class) + public static class TypeCohortReportAction extends SpecimenVisitReportAction + { + public TypeCohortReportAction() + { + super(TypeCohortReportFactory.class); + } + // no implementation needed; this action exists only to provide an entry point + // with request->bean translation for this report type. + } +} diff --git a/specimen/src/org/labkey/specimen/pipeline/SpecimenBatch.java b/specimen/src/org/labkey/specimen/pipeline/SpecimenBatch.java new file mode 100644 index 00000000..4fe5dcdf --- /dev/null +++ b/specimen/src/org/labkey/specimen/pipeline/SpecimenBatch.java @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2008-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.specimen.pipeline; + +import org.labkey.api.admin.PipelineJobLoggerGetter; +import org.labkey.api.pipeline.PipeRoot; +import org.labkey.api.pipeline.PipelineJobService; +import org.labkey.api.pipeline.TaskId; +import org.labkey.api.pipeline.TaskPipeline; +import org.labkey.api.portal.ProjectUrls; +import org.labkey.api.specimen.SpecimensPage; +import org.labkey.api.specimen.pipeline.SpecimenJobSupport; +import org.labkey.api.study.importer.SimpleStudyImportContext; +import org.labkey.api.study.pipeline.StudyBatch; +import org.labkey.api.util.FileUtil; +import org.labkey.api.util.PageFlowUtil; +import org.labkey.api.view.ActionURL; +import org.labkey.api.view.ViewBackgroundInfo; + +import java.io.File; +import java.io.Serializable; + +/** + * User: brittp + * Date: Mar 14, 2006 + * Time: 5:04:38 PM + */ + +// Pipeline job used for importing individual specimen archives (not as part of a study). +public class SpecimenBatch extends StudyBatch implements Serializable, SpecimenJobSupport +{ + private boolean _isMerge; + + // For serialization + protected SpecimenBatch() {} + + public SpecimenBatch(ViewBackgroundInfo info, File definitionFile, PipeRoot root, boolean merge) + { + super(info, definitionFile, root); + _isMerge = merge; + } + + @Override + protected File createLogFile() + { + return new File(getPipeRoot().getLogDirectory(), FileUtil.makeFileNameWithTimestamp(_definitionFile.getName(), "log")); + } + + @Override + public String getDescription() + { + String description = "Import specimens"; + if (_definitionFile != null) + description += ": " + _definitionFile.getName(); + return description; + } + + @Override + public ActionURL getStatusHref() + { + return PageFlowUtil.urlProvider(ProjectUrls.class).getBeginURL(getInfo().getContainer(), SpecimensPage.PAGE_ID); + } + + @Override + public File getSpecimenArchive() + { + return _definitionFile; + } + + @Override + public boolean isMerge() + { + return _isMerge; + } + + @Override + public SimpleStudyImportContext getImportContext() + { + return new SimpleStudyImportContext(getUser(), getContainer(), null, null, new PipelineJobLoggerGetter(this), null); + } + + @Override + public TaskPipeline getTaskPipeline() + { + return PipelineJobService.get().getTaskPipeline(new TaskId(SpecimenBatch.class)); + } +} \ No newline at end of file diff --git a/specimen/src/org/labkey/specimen/pipeline/SpecimenPipeline.java b/specimen/src/org/labkey/specimen/pipeline/SpecimenPipeline.java new file mode 100644 index 00000000..2919c888 --- /dev/null +++ b/specimen/src/org/labkey/specimen/pipeline/SpecimenPipeline.java @@ -0,0 +1,57 @@ +package org.labkey.specimen.pipeline; + +import org.labkey.api.module.Module; +import org.labkey.api.pipeline.PipeRoot; +import org.labkey.api.pipeline.PipelineDirectory; +import org.labkey.api.pipeline.PipelineProvider; +import org.labkey.api.security.permissions.InsertPermission; +import org.labkey.api.specimen.pipeline.AbstractSpecimenTask; +import org.labkey.api.study.SpecimenService; +import org.labkey.api.study.SpecimenTransform; +import org.labkey.api.study.StudyService; +import org.labkey.api.view.ViewContext; +import org.labkey.specimen.actions.SpecimenController2.ImportSpecimenDataAction; + +import java.io.File; + +public class SpecimenPipeline extends PipelineProvider +{ + public SpecimenPipeline(Module owningModule) + { + super("Specimen", owningModule); + } + + @Override + public void updateFileProperties(final ViewContext context, PipeRoot pr, PipelineDirectory directory, boolean includeAll) + { + if ( + !context.getContainer().hasPermission(context.getUser(), InsertPermission.class) || + context.getContainer().isDataspace() || // Cannot import specimens into Dataspace container + StudyService.get().getStudy(context.getContainer()) == null) + { + return; + } + + File[] files = directory.listFiles(new FileEntryFilter() + { + @Override + public boolean accept(File f) + { + if (AbstractSpecimenTask.ARCHIVE_FILE_TYPE.isType(f)) + return true; + else + { + for (SpecimenTransform transform : SpecimenService.get().getSpecimenTransforms(context.getContainer())) + { + if (transform.getFileType().isType(f)) + return true; + } + } + return false; + } + }); + + String actionId = createActionId(ImportSpecimenDataAction.class, "Import Specimen Data"); + addAction(actionId, ImportSpecimenDataAction.class, "Import Specimen Data", directory, files, true, false, includeAll); + } +} diff --git a/specimen/src/org/labkey/specimen/pipeline/SpecimenReloadJob.java b/specimen/src/org/labkey/specimen/pipeline/SpecimenReloadJob.java index 55d74a31..c635f80c 100644 --- a/specimen/src/org/labkey/specimen/pipeline/SpecimenReloadJob.java +++ b/specimen/src/org/labkey/specimen/pipeline/SpecimenReloadJob.java @@ -19,7 +19,6 @@ import org.labkey.api.pipeline.PipelineJobService; import org.labkey.api.pipeline.TaskId; import org.labkey.api.pipeline.TaskPipeline; -import org.labkey.api.specimen.pipeline.SpecimenBatch; import org.labkey.api.study.SpecimenTransform; import org.labkey.api.util.FileUtil; import org.labkey.api.view.ViewBackgroundInfo; diff --git a/specimen/src/org/labkey/specimen/report/RequestSummaryByVisitType.java b/specimen/src/org/labkey/specimen/report/RequestSummaryByVisitType.java new file mode 100644 index 00000000..b623ecb2 --- /dev/null +++ b/specimen/src/org/labkey/specimen/report/RequestSummaryByVisitType.java @@ -0,0 +1,27 @@ +package org.labkey.specimen.report; + +public class RequestSummaryByVisitType extends SummaryByVisitType +{ + private Integer _destinationSiteId; + private String _siteLabel; + + public Integer getDestinationSiteId() + { + return _destinationSiteId; + } + + public void setDestinationSiteId(Integer destinationSiteId) + { + _destinationSiteId = destinationSiteId; + } + + public String getSiteLabel() + { + return _siteLabel; + } + + public void setSiteLabel(String siteLabel) + { + _siteLabel = siteLabel; + } +} diff --git a/specimen/src/org/labkey/specimen/report/SpecimenCountSummary.java b/specimen/src/org/labkey/specimen/report/SpecimenCountSummary.java new file mode 100644 index 00000000..06fd68c8 --- /dev/null +++ b/specimen/src/org/labkey/specimen/report/SpecimenCountSummary.java @@ -0,0 +1,60 @@ +/* + * 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.specimen.report; + +/** + * User: brittp + * Created: Jan 29, 2008 5:27:13 PM + */ +public class SpecimenCountSummary implements SpecimenReportCellData +{ + private Long _vialCount; + private Double _totalVolume; + private Integer _visit; + + @Override + public Long getVialCount() + { + return _vialCount; + } + + public void setVialCount(Long vialCount) + { + _vialCount = vialCount; + } + + @Override + public Double getTotalVolume() + { + return _totalVolume; + } + + public void setTotalVolume(Double totalVolume) + { + _totalVolume = totalVolume; + } + + @Override + public Integer getVisit() + { + return _visit; + } + + public void setVisit(Integer visit) + { + _visit = visit; + } +} diff --git a/specimen/src/org/labkey/specimen/report/SpecimenDetailQueryHelper.java b/specimen/src/org/labkey/specimen/report/SpecimenDetailQueryHelper.java new file mode 100644 index 00000000..33f19022 --- /dev/null +++ b/specimen/src/org/labkey/specimen/report/SpecimenDetailQueryHelper.java @@ -0,0 +1,34 @@ +package org.labkey.specimen.report; + +import org.labkey.api.data.SQLFragment; + +import java.util.Map; + +public class SpecimenDetailQueryHelper +{ + private final SQLFragment _viewSql; + private final String _typeGroupingColumns; + private final Map _aliasToTypePropertyMap; + + public SpecimenDetailQueryHelper(SQLFragment viewSql, String typeGroupingColumns, Map aliasToTypePropertyMap) + { + _viewSql = viewSql; + _typeGroupingColumns = typeGroupingColumns; + _aliasToTypePropertyMap = aliasToTypePropertyMap; + } + + public SQLFragment getViewSql() + { + return _viewSql; + } + + public String getTypeGroupingColumns() + { + return _typeGroupingColumns; + } + + public Map getAliasToTypePropertyMap() + { + return _aliasToTypePropertyMap; + } +} diff --git a/specimen/src/org/labkey/specimen/report/SpecimenReportCellData.java b/specimen/src/org/labkey/specimen/report/SpecimenReportCellData.java new file mode 100644 index 00000000..2a87a79b --- /dev/null +++ b/specimen/src/org/labkey/specimen/report/SpecimenReportCellData.java @@ -0,0 +1,27 @@ +/* + * 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.specimen.report; + +/** + * User: brittp + * Created: Jan 14, 2008 10:23:30 AM + */ +public interface SpecimenReportCellData +{ + Long getVialCount(); + Double getTotalVolume(); + Integer getVisit(); +} diff --git a/specimen/src/org/labkey/specimen/report/SpecimenReportExcelWriter.java b/specimen/src/org/labkey/specimen/report/SpecimenReportExcelWriter.java new file mode 100644 index 00000000..e72c8ba3 --- /dev/null +++ b/specimen/src/org/labkey/specimen/report/SpecimenReportExcelWriter.java @@ -0,0 +1,154 @@ +/* + * 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.specimen.report; + +import jxl.Workbook; +import jxl.WorkbookSettings; +import jxl.write.Label; +import jxl.write.Number; +import jxl.write.WritableCell; +import jxl.write.WritableCellFormat; +import jxl.write.WritableFont; +import jxl.write.WritableSheet; +import jxl.write.WritableWorkbook; +import jxl.write.WriteException; +import org.labkey.api.data.ExcelWriter; +import org.labkey.api.study.Visit; +import org.labkey.api.util.ExceptionUtil; + +import javax.servlet.ServletOutputStream; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.Collection; +import java.util.List; + +/* + * User: brittp + * Date: May 20, 2008 + * Time: 9:34:36 AM + */ +public class SpecimenReportExcelWriter +{ + private final SpecimenVisitReportParameters _parameters; + + private WritableCellFormat _headerFormat; + + public SpecimenReportExcelWriter(SpecimenVisitReportParameters parameters) + { + _parameters = parameters; + } + + public void write(HttpServletResponse response) + { + WritableWorkbook workbook = null; + ServletOutputStream ostream = null; + try + { + WritableFont boldFont = new WritableFont(WritableFont.ARIAL, 10, WritableFont.BOLD); + _headerFormat = new WritableCellFormat(boldFont); + _headerFormat.setWrap(true); + _headerFormat.setVerticalAlignment(jxl.format.VerticalAlignment.TOP); + ostream = ExcelWriter.getOutputStream(response, _parameters.getLabel(), ExcelWriter.ExcelDocumentType.xls); + + WorkbookSettings settings = new WorkbookSettings(); + settings.setArrayGrowSize(300000); + workbook = Workbook.createWorkbook(ostream, settings); + + for (SpecimenVisitReport report : _parameters.getReports()) + writeReport(workbook, report); + } + catch (WriteException | IOException e) + { + throw new RuntimeException(e); + } + finally + { + if (workbook != null) + { + try + { + workbook.write(); + workbook.close(); + + // Flush the OutputStream + ostream.flush(); + // Finally, close the OutputStream + ostream.close(); + } + catch (WriteException | IOException e) + { + ExceptionUtil.logExceptionToMothership(null, e); + } + } + } + } + + private void writeReport(WritableWorkbook workbook, SpecimenVisitReport report) throws WriteException + { + String sheetName = ExcelWriter.cleanSheetName(report.getTitle()); + for (int i = 2; workbook.getSheet(sheetName) != null; i++) + sheetName = sheetName.substring(0, sheetName.length() - 1) + i; + + WritableSheet sheet = workbook.createSheet(sheetName, workbook.getNumberOfSheets()); + + List visits = report.getVisits(); + + // Merge cells at top of sheet and write the headers + // One or more embedded tabs split a header into equally spaced columns + sheet.mergeCells(0, 0, 10, 0); + WritableCell cell = new Label(0, 0, _parameters.getLabel() + ": " + report.getTitle()); + sheet.addCell(cell); + + for (int i = 0; i < visits.size(); i++) + sheet.addCell(new Label(i + report.getLabelDepth(), 1, visits.get(i).getDisplayString(), _headerFormat)); + + boolean numericData = report.isNumericData(); + Collection rows = (Collection) report.getRows(); + if (rows.isEmpty()) + sheet.addCell(new Label(report.getLabelDepth() + 1, 2, "No data to show")); + else + { + int rowIndex = 2; + for (SpecimenVisitReport.Row rowData : rows) + { + int maxRowHeight = rowData.getMaxExcelRowHeight(visits); + int columnIndex = 0; + + for (SpecimenReportTitle titleElement : rowData.getTitleHierarchy()) + sheet.addCell(new Label(columnIndex++, rowIndex, titleElement.getDisplayValue())); + + for (Visit visit : visits) + { + String[] valueSet = rowData.getCellExcelText(visit); + if (valueSet != null && valueSet.length > 0) + { + for (int i = 0; i < valueSet.length; i++) + { + String value = valueSet[i]; + sheet.addCell(numericData ? + new Number(columnIndex, rowIndex + i, Double.parseDouble(value)) : + new Label(columnIndex, rowIndex + i, value)); + } + } + columnIndex++; + } + rowIndex += maxRowHeight; + } + } + sheet.getSettings().setVerticalFreeze(2); + sheet.getSettings().setHorizontalFreeze(report.getLabelDepth()); + } +} \ No newline at end of file diff --git a/specimen/src/org/labkey/specimen/report/SpecimenReportManager.java b/specimen/src/org/labkey/specimen/report/SpecimenReportManager.java new file mode 100644 index 00000000..3dfc249b --- /dev/null +++ b/specimen/src/org/labkey/specimen/report/SpecimenReportManager.java @@ -0,0 +1,377 @@ +package org.labkey.specimen.report; + +import org.apache.commons.beanutils.PropertyUtils; +import org.apache.logging.log4j.LogManager; +import org.labkey.api.data.ColumnInfo; +import org.labkey.api.data.Container; +import org.labkey.api.data.SQLFragment; +import org.labkey.api.data.SimpleFilter; +import org.labkey.api.data.SqlSelector; +import org.labkey.api.data.Table; +import org.labkey.api.data.TableInfo; +import org.labkey.api.data.dialect.SqlDialect; +import org.labkey.api.query.CustomView; +import org.labkey.api.query.FieldKey; +import org.labkey.api.query.QueryService; +import org.labkey.api.query.UserSchema; +import org.labkey.api.security.User; +import org.labkey.api.specimen.SpecimenManager; +import org.labkey.api.specimen.SpecimenQuerySchema; +import org.labkey.api.specimen.SpecimenSchema; +import org.labkey.api.study.CohortFilter; +import org.labkey.api.study.StudyService; +import org.labkey.api.view.ActionURL; + +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; + +public class SpecimenReportManager +{ + private final static SpecimenReportManager INSTANCE = new SpecimenReportManager(); + + private SpecimenReportManager() + { + } + + public static SpecimenReportManager get() + { + return INSTANCE; + } + + private SpecimenDetailQueryHelper getSpecimenDetailQueryHelper(Container container, User user, + CustomView baseView, SimpleFilter specimenDetailFilter, + SpecimenTypeLevel level) + { + UserSchema schema = SpecimenQuerySchema.get(StudyService.get().getStudy(container), user); + TableInfo tinfo = schema.getTable(SpecimenQuerySchema.SPECIMEN_DETAIL_TABLE_NAME); + + Map aliasToTypeProperty = new LinkedHashMap<>(); + + Collection columns = new HashSet<>(); + if (baseView != null) + { + // copy our saved view filter into our SimpleFilter via an ActionURL (yuck...) + ActionURL url = new ActionURL(); + baseView.applyFilterAndSortToURL(url, "mockDataRegion"); + specimenDetailFilter.addUrlFilters(url, "mockDataRegion"); + } + + // Build a list fo FieldKeys for all the columns that we must select, + // regardless of whether they're in the selected specimen view. We need to ask the view which + // columns are required in case there's a saved filter on a column outside the primary table: + columns.add(FieldKey.fromParts("Container")); + columns.add(FieldKey.fromParts("Visit")); + columns.add(FieldKey.fromParts("SequenceNum")); + columns.add(FieldKey.fromParts("LockedInRequest")); + columns.add(FieldKey.fromParts("GlobalUniqueId")); + columns.add(FieldKey.fromParts(StudyService.get().getSubjectColumnName(container))); + if (StudyService.get().showCohorts(container, schema.getUser())) + columns.add(FieldKey.fromParts("CollectionCohort")); + columns.add(FieldKey.fromParts("Volume")); + if (level != null) + { + for (SpecimenTypeBeanProperty typeProperty : level.getGroupingColumns()) + columns.add(typeProperty.getTypeKey()); + } + + // turn our fieldkeys into columns: + Map colMap = QueryService.get().getColumns(tinfo, columns); + Set unresolvedColumns = new HashSet<>(); + Collection cols = new ArrayList<>(colMap.values()); + cols = QueryService.get().ensureRequiredColumns(tinfo, cols, specimenDetailFilter, null, unresolvedColumns); + if (!unresolvedColumns.isEmpty()) + throw new IllegalStateException("Unable to resolve column(s): " + unresolvedColumns.toString()); + // generate our select SQL: + SQLFragment viewSql = new SQLFragment(); + viewSql.appendComment("", tinfo.getSqlDialect()); + viewSql.append(Table.getSelectSQL(tinfo, cols, specimenDetailFilter, null)); + viewSql.appendComment("", tinfo.getSqlDialect()); + + // save off the aliases for our grouping columns, so we can group by them later: + String groupingColSql = null; + if (level != null) + { + StringBuilder builder = new StringBuilder(); + String sep = ""; + for (SpecimenTypeBeanProperty typeProperty : level.getGroupingColumns()) + { + ColumnInfo col = colMap.get(typeProperty.getTypeKey()); + builder.append(sep).append(col.getAlias()); + sep = ", "; + aliasToTypeProperty.put(col.getAlias(), typeProperty); + } + groupingColSql = builder.toString(); + } + return new SpecimenDetailQueryHelper(viewSql, groupingColSql, aliasToTypeProperty); + } + + public SummaryByVisitType[] getSpecimenSummaryByVisitType(Container container, User user, SimpleFilter specimenDetailFilter, + boolean includeParticipantGroups, SpecimenTypeLevel level, CustomView baseView) + { + if (specimenDetailFilter == null) + specimenDetailFilter = new SimpleFilter(); + else + { + SimpleFilter clone = new SimpleFilter(); + clone.addAllClauses(specimenDetailFilter); + specimenDetailFilter = clone; + } + + final SpecimenDetailQueryHelper viewSqlHelper = getSpecimenDetailQueryHelper(container, user, baseView, specimenDetailFilter, level); + + String perPtidSpecimenSQL = "\t-- Inner SELECT gets the number of vials per participant/visit/type:\n" + + "\tSELECT InnerView.Container, InnerView.Visit, " + viewSqlHelper.getTypeGroupingColumns() + ",\n" + + "\tInnerView." + StudyService.get().getSubjectColumnName(container) + ", COUNT(*) AS VialCount, SUM(InnerView.Volume) AS PtidVolume \n" + + "FROM (\n" + viewSqlHelper.getViewSql().getSQL() + "\n) InnerView\n" + + "\tGROUP BY InnerView.Container, InnerView." + StudyService.get().getSubjectColumnName(container) + + ", InnerView.Visit, " + viewSqlHelper.getTypeGroupingColumns() + "\n"; + + SQLFragment sql = new SQLFragment("-- Outer grouping allows us to count participants AND sum vial counts:\n" + + "SELECT VialData.Visit AS Visit, " + viewSqlHelper.getTypeGroupingColumns() + ", COUNT(*) as ParticipantCount, \n" + + "SUM(VialData.VialCount) AS VialCount, SUM(VialData.PtidVolume) AS TotalVolume FROM \n" + + "(\n" + perPtidSpecimenSQL + ") AS VialData\n" + + "GROUP BY Visit, " + viewSqlHelper.getTypeGroupingColumns() + "\n" + + "ORDER BY " + viewSqlHelper.getTypeGroupingColumns() + ", Visit"); + sql.addAll(viewSqlHelper.getViewSql().getParamsArray()); + + final List ret = new ArrayList<>(); + + new SqlSelector(SpecimenSchema.get().getSchema(), sql).forEach(rs -> { + SummaryByVisitType summary = new SummaryByVisitType(); + if (rs.getObject("Visit") != null) + summary.setVisit(rs.getInt("Visit")); + summary.setTotalVolume(rs.getDouble("TotalVolume")); + Double vialCount = rs.getDouble("VialCount"); + summary.setVialCount(vialCount.longValue()); + Double participantCount = rs.getDouble("ParticipantCount"); + summary.setParticipantCount(participantCount.longValue()); + + for (Map.Entry typeProperty : viewSqlHelper.getAliasToTypePropertyMap().entrySet()) + { + String value = rs.getString(typeProperty.getKey()); + try + { + PropertyUtils.setProperty(summary, typeProperty.getValue().getBeanProperty(), value); + } + catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) + { + throw new RuntimeException(e); + } + } + ret.add(summary); + }); + + SummaryByVisitType[] summaries = ret.toArray(new SummaryByVisitType[ret.size()]); + + if (includeParticipantGroups) + setSummaryParticipantGroups(perPtidSpecimenSQL, viewSqlHelper.getViewSql().getParamsArray(), + viewSqlHelper.getAliasToTypePropertyMap(), summaries, StudyService.get().getSubjectColumnName(container), "Visit"); + + return summaries; + } + + private void setSummaryParticipantGroups(String sql, Object[] paramArray, final Map aliasToTypeProperty, + SummaryByVisitType[] summaries, final String ptidColumnName, final String visitValueColumnName) + { + SQLFragment fragment = new SQLFragment(sql); + fragment.addAll(paramArray); + + final Map> cellToPtidSet = new HashMap<>(); + + new SqlSelector(SpecimenSchema.get().getSchema(), fragment).forEach(rs -> { + String ptid = rs.getString(ptidColumnName); + Integer visit = rs.getInt(visitValueColumnName); + String primaryType = null; + String derivative = null; + String additive = null; + + for (Map.Entry entry : aliasToTypeProperty.entrySet()) + { + switch (entry.getValue().getLevel()) + { + case PrimaryType: + primaryType = rs.getString(entry.getKey()); + break; + case Derivative: + derivative = rs.getString(entry.getKey()); + break; + case Additive: + additive = rs.getString(entry.getKey()); + break; + } + } + + String key = getPtidListKey(visit, primaryType, derivative, additive); + + Set ptids = cellToPtidSet.computeIfAbsent(key, k -> new TreeSet<>()); + ptids.add(ptid != null ? ptid : "[unknown]"); + }); + + for (SummaryByVisitType summary : summaries) + { + Integer visit = summary.getVisit(); + String key = getPtidListKey(visit, summary.getPrimaryType(), summary.getDerivative(), summary.getAdditive()); + Set ptids = cellToPtidSet.get(key); + summary.setParticipantIds(ptids); + } + } + + private String getPtidListKey(Integer visit, String primaryType, String derivativeType, String additiveType) + { + return visit + "/" + primaryType + "/" + + (derivativeType != null ? derivativeType : "all") + + (additiveType != null ? additiveType : "all"); + } + + public RequestSummaryByVisitType[] getRequestSummaryBySite(Container container, User user, SimpleFilter specimenDetailFilter, boolean includeParticipantGroups, SpecimenTypeLevel level, CustomView baseView, boolean completeRequestsOnly) + { + if (specimenDetailFilter == null) + { + specimenDetailFilter = new SimpleFilter(); + } + else + { + SimpleFilter clone = new SimpleFilter(); + clone.addAllClauses(specimenDetailFilter); + specimenDetailFilter = clone; + } + + final SpecimenDetailQueryHelper sqlHelper = getSpecimenDetailQueryHelper(container, user, baseView, specimenDetailFilter, level); + + TableInfo locationTableInfo = SpecimenSchema.get().getTableInfoLocation(container); + String subjectCol = StudyService.get().getSubjectColumnName(container); + String sql = "SELECT Specimen.Container,\n" + + "Specimen." + subjectCol + ",\n" + + "Request.DestinationSiteId,\n" + + "Site.Label AS SiteLabel,\n" + + "Visit AS Visit,\n" + + sqlHelper.getTypeGroupingColumns() + ", COUNT(*) AS VialCount, SUM(Volume) AS TotalVolume\n" + + "FROM (" + sqlHelper.getViewSql().getSQL() + ") AS Specimen\n" + + "JOIN study.SampleRequestSpecimen AS RequestSpecimen ON \n" + + "\tSpecimen.GlobalUniqueId = RequestSpecimen.SpecimenGlobalUniqueId AND\n" + + "\tSpecimen.Container = RequestSpecimen.Container\n" + + "JOIN study.SampleRequest AS Request ON\n" + + "\tRequestSpecimen.SampleRequestId = Request.RowId AND\n" + + "\tRequestSpecimen.Container = Request.Container\n" + + "JOIN " + locationTableInfo.getSelectName() + " AS Site ON\n" + + "\tSite.Container = Request.Container AND\n" + + "\tSite.RowId = Request.DestinationSiteId\n" + + "JOIN study.SampleRequestStatus AS Status ON\n" + + "\tStatus.Container = Request.Container AND\n" + + "\tStatus.RowId = Request.StatusId and Status.SpecimensLocked = ?\n" + + (completeRequestsOnly ? "\tAND Status.FinalState = ?\n" : "") + + "GROUP BY Specimen.Container, Specimen." + subjectCol + ", Site.Label, DestinationSiteId, " + sqlHelper.getTypeGroupingColumns() + ", Visit\n" + + "ORDER BY Specimen.Container, Specimen." + subjectCol + ", Site.Label, DestinationSiteId, " + sqlHelper.getTypeGroupingColumns() + ", Visit"; + + Object[] params = new Object[sqlHelper.getViewSql().getParamsArray().length + 1 + (completeRequestsOnly ? 1 : 0)]; + System.arraycopy(sqlHelper.getViewSql().getParamsArray(), 0, params, 0, sqlHelper.getViewSql().getParamsArray().length); + params[params.length - 1] = Boolean.TRUE; + if (completeRequestsOnly) + params[params.length - 2] = Boolean.TRUE; + + SqlDialect d = SpecimenSchema.get().getSqlDialect(); + SQLFragment fragment = new SQLFragment(); + fragment.appendComment("",d); + fragment.append(sql); + fragment.appendComment("", d); + fragment.addAll(params); + + final List ret = new ArrayList<>(); + + new SqlSelector(SpecimenSchema.get().getSchema(), fragment).forEach(rs -> { + RequestSummaryByVisitType summary = new RequestSummaryByVisitType(); + summary.setDestinationSiteId(rs.getInt("DestinationSiteId")); + summary.setSiteLabel(rs.getString("SiteLabel")); + summary.setVisit(rs.getInt("Visit")); + summary.setTotalVolume(rs.getDouble("TotalVolume")); + Double vialCount = rs.getDouble("VialCount"); + summary.setVialCount(vialCount.longValue()); + + for (Map.Entry typeProperty : sqlHelper.getAliasToTypePropertyMap().entrySet()) + { + String value = rs.getString(typeProperty.getKey()); + + try + { + PropertyUtils.setProperty(summary, typeProperty.getValue().getBeanProperty(), value); + } + catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) + { + LogManager.getLogger(SpecimenManager.class).error(e); + } + } + ret.add(summary); + }); + + RequestSummaryByVisitType[] summaries = ret.toArray(new RequestSummaryByVisitType[ret.size()]); + + if (includeParticipantGroups) + setSummaryParticipantGroups(sql, params, null, summaries, subjectCol, "Visit"); + + return summaries; + } + public Collection getParticipantSummaryByVisitType(Container container, User user, + SimpleFilter specimenDetailFilter, CustomView baseView, CohortFilter.Type cohortType) + { + if (specimenDetailFilter == null) + specimenDetailFilter = new SimpleFilter(); + else + { + SimpleFilter clone = new SimpleFilter(); + clone.addAllClauses(specimenDetailFilter); + specimenDetailFilter = clone; + } + SpecimenDetailQueryHelper sqlHelper = getSpecimenDetailQueryHelper(container, user, baseView, specimenDetailFilter, null); + String subjectCol = StudyService.get().getSubjectColumnName(container); + SQLFragment cohortJoinClause = null; + switch (cohortType) + { + case DATA_COLLECTION: + cohortJoinClause = new SQLFragment( + "LEFT OUTER JOIN study.ParticipantVisit ON\n " + + "\tSpecimenQuery.SequenceNum = study.ParticipantVisit.SequenceNum AND\n" + + "\tSpecimenQuery." + subjectCol + " = study.ParticipantVisit.ParticipantId AND\n" + + "\tSpecimenQuery.Container = study.ParticipantVisit.Container\n" + + "LEFT OUTER JOIN study.Cohort ON \n" + + "\tstudy.ParticipantVisit.CohortId = study.Cohort.RowId AND\n" + + "\tstudy.ParticipantVisit.Container = study.Cohort.Container\n" + ); + break; + case PTID_CURRENT: + cohortJoinClause = new SQLFragment( + "LEFT OUTER JOIN study.Cohort ON \n" + + "\tstudy.Participant.CurrentCohortId = study.Cohort.RowId AND\n" + + "\tstudy.Participant.Container = study.Cohort.Container\n" + ); + break; + case PTID_INITIAL: + cohortJoinClause = new SQLFragment( + "LEFT OUTER JOIN study.Cohort ON \n" + + "\tstudy.Participant.InitialCohortId = study.Cohort.RowId AND\n" + + "\tstudy.Participant.Container = study.Cohort.Container\n" + ); + break; + } + + SQLFragment ptidSpecimenSQL = new SQLFragment(); + SqlDialect d = SpecimenSchema.get().getSqlDialect(); + ptidSpecimenSQL.appendComment("", d); + ptidSpecimenSQL.append("SELECT SpecimenQuery.Visit AS Visit, SpecimenQuery.").append(subjectCol).append(" AS ParticipantId,\n") + .append("COUNT(*) AS VialCount, study.Cohort.Label AS Cohort, SUM(SpecimenQuery.Volume) AS TotalVolume\n").append("FROM ("); + ptidSpecimenSQL.append(sqlHelper.getViewSql()); + ptidSpecimenSQL.append(") AS SpecimenQuery\n" + "LEFT OUTER JOIN study.Participant ON\n" + "\tSpecimenQuery.").append(subjectCol).append(" = study.Participant.ParticipantId AND\n").append("\tSpecimenQuery.Container = study.Participant.Container\n"); + ptidSpecimenSQL.append(cohortJoinClause); + ptidSpecimenSQL.append("GROUP BY study.Cohort.Label, SpecimenQuery.").append(subjectCol).append(", Visit\n").append("ORDER BY study.Cohort.Label, SpecimenQuery.").append(subjectCol).append(", Visit"); + ptidSpecimenSQL.appendComment("", d); + + return new SqlSelector(SpecimenSchema.get().getSchema(), ptidSpecimenSQL).getCollection(SummaryByVisitParticipant.class); + } +} diff --git a/specimen/src/org/labkey/specimen/report/SpecimenReportTitle.java b/specimen/src/org/labkey/specimen/report/SpecimenReportTitle.java new file mode 100644 index 00000000..435a163a --- /dev/null +++ b/specimen/src/org/labkey/specimen/report/SpecimenReportTitle.java @@ -0,0 +1,55 @@ +/* + * 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.specimen.report; + +import org.apache.commons.lang3.StringUtils; + +/** + * User: adam + * Date: 12/12/11 + * Time: 1:40 PM + */ + +// Some specimen reports coalesce title columns vertically instead of repeating the same information in every row. This +// is a problem in demo mode, where all PTIDs of the same length resolve to the same display value. This class was added +// to distinguish "value" (the underlying value we use to coalesce) from "display value" (what we display to the user). +public class SpecimenReportTitle +{ + private final String _value; + private final String _displayValue; + + // Simple case -- value and display value are the same + public SpecimenReportTitle(String value) + { + this(value, value); + } + + public SpecimenReportTitle(String value, String displayValue) + { + _value = StringUtils.trimToEmpty(value); + _displayValue = StringUtils.trimToEmpty(displayValue); + } + + public String getValue() + { + return _value; + } + + public String getDisplayValue() + { + return _displayValue; + } +} diff --git a/specimen/src/org/labkey/specimen/report/SpecimenTypeBeanProperty.java b/specimen/src/org/labkey/specimen/report/SpecimenTypeBeanProperty.java new file mode 100644 index 00000000..e48d48a4 --- /dev/null +++ b/specimen/src/org/labkey/specimen/report/SpecimenTypeBeanProperty.java @@ -0,0 +1,32 @@ +package org.labkey.specimen.report; + +import org.labkey.api.query.FieldKey; + +public class SpecimenTypeBeanProperty +{ + private final FieldKey _typeKey; + private final String _beanProperty; + private final SpecimenTypeLevel _level; + + public SpecimenTypeBeanProperty(FieldKey typeKey, String beanProperty, SpecimenTypeLevel level) + { + _typeKey = typeKey; + _beanProperty = beanProperty; + _level = level; + } + + public FieldKey getTypeKey() + { + return _typeKey; + } + + public String getBeanProperty() + { + return _beanProperty; + } + + public SpecimenTypeLevel getLevel() + { + return _level; + } +} diff --git a/specimen/src/org/labkey/specimen/report/SpecimenTypeLevel.java b/specimen/src/org/labkey/specimen/report/SpecimenTypeLevel.java new file mode 100644 index 00000000..bdf113c3 --- /dev/null +++ b/specimen/src/org/labkey/specimen/report/SpecimenTypeLevel.java @@ -0,0 +1,83 @@ +package org.labkey.specimen.report; + +import org.labkey.api.query.FieldKey; +import org.labkey.api.util.SafeToRenderEnum; + +import java.util.ArrayList; +import java.util.List; + +public enum SpecimenTypeLevel implements SafeToRenderEnum +{ + PrimaryType() + { + @Override + public List getGroupingColumns() + { + List list = new ArrayList<>(); + list.add(new SpecimenTypeBeanProperty(FieldKey.fromParts("PrimaryType", "Description"), "primaryType", this)); + return list; + } + + @Override + public String[] getTitleHierarchy(SummaryByVisitType summary) + { + return new String[]{summary.getPrimaryType()}; + } + + @Override + public String getLabel() + { + return "Primary Type"; + } + }, + Derivative() + { + @Override + public List getGroupingColumns() + { + List parent = SpecimenTypeLevel.PrimaryType.getGroupingColumns(); + parent.add(new SpecimenTypeBeanProperty(FieldKey.fromParts("DerivativeType", "Description"), "derivative", this)); + return parent; + } + + @Override + public String[] getTitleHierarchy(SummaryByVisitType summary) + { + return new String[]{summary.getPrimaryType(), summary.getDerivative()}; + } + + @Override + public String getLabel() + { + return "Derivative"; + } + }, + Additive() + { + @Override + public List getGroupingColumns() + { + List parent = SpecimenTypeLevel.Derivative.getGroupingColumns(); + parent.add(new SpecimenTypeBeanProperty(FieldKey.fromParts("AdditiveType", "Description"), "additive", this)); + return parent; + } + + @Override + public String[] getTitleHierarchy(SummaryByVisitType summary) + { + return new String[]{summary.getPrimaryType(), summary.getDerivative(), summary.getAdditive()}; + } + + @Override + public String getLabel() + { + return "Additive"; + } + }; + + public abstract String[] getTitleHierarchy(SummaryByVisitType summary); + + public abstract List getGroupingColumns(); + + public abstract String getLabel(); +} diff --git a/specimen/src/org/labkey/specimen/report/SpecimenTypeVisitReport.java b/specimen/src/org/labkey/specimen/report/SpecimenTypeVisitReport.java new file mode 100644 index 00000000..a71c4c68 --- /dev/null +++ b/specimen/src/org/labkey/specimen/report/SpecimenTypeVisitReport.java @@ -0,0 +1,168 @@ +/* + * 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.specimen.report; + +import org.labkey.api.data.SimpleFilter; +import org.labkey.api.study.SpecimenUrls; +import org.labkey.api.study.Visit; +import org.labkey.api.util.DemoMode; +import org.labkey.api.util.Formats; +import org.labkey.api.util.PageFlowUtil; +import org.labkey.api.view.ActionURL; +import org.labkey.specimen.actions.SpecimenReportActions; + +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +/** + * User: brittp + * Created: Jan 14, 2008 9:44:25 AM + */ +public class SpecimenTypeVisitReport extends SpecimenVisitReport +{ + public SpecimenTypeVisitReport(String titlePrefix, List visits, SimpleFilter filter, SpecimenVisitReportParameters parameters) + { + super(titlePrefix, visits, filter, parameters); + } + + @Override + public Collection createRows() + { + SpecimenTypeLevel level = getTypeLevelEnum(); + SummaryByVisitType[] countSummary = SpecimenReportManager.get().getSpecimenSummaryByVisitType(_container, getUser(), _filter, isViewPtidList(), level, getBaseCustomView()); + Map rows = new TreeMap<>(); + + for (SummaryByVisitType count : countSummary) + { + String key = count.getPrimaryType() + "/" + + (count.getDerivative() != null ? count.getDerivative() : "All") + "/" + + (count.getAdditive() != null ? count.getAdditive() : "All"); + Row row = rows.get(key); + if (row == null) + { + String[] titleHierarchy = level.getTitleHierarchy(count); + row = new Row(titleHierarchy); + rows.put(key, row); + } + setVisitAsNonEmpty(count.getVisit()); + row.add(count); + } + + return rows.values(); + } + + private String getCellSummaryText(SummaryByVisitType summary) + { + StringBuilder summaryString = new StringBuilder(); + if (isViewVialCount()) + summaryString.append(summary.getVialCount()); + if (isViewParticipantCount()) + { + if (summaryString.length() > 0) + summaryString.append("/"); + summaryString.append(summary.getParticipantCount()); + } + if (isViewVolume()) + { + if (summaryString.length() > 0) + summaryString.append("/"); + //summaryString.append(summary.getTotalVolume()); + summaryString.append(Formats.f2.format(summary.getTotalVolume())); + } + return summaryString.toString(); + } + + @Override + protected String[] getCellExcelText(Visit visit, SummaryByVisitType summary) + { + if (summary == null || summary.getVialCount() == null) + return new String[] {}; + String summaryText = getCellSummaryText(summary); + boolean hasSummaryText = summaryText != null && summaryText.length() > 0; + int ptidCount = isViewPtidList() ? summary.getParticipantIds().size() : 0; + String[] strArray = new String[ptidCount + (hasSummaryText ? 1 : 0)]; + if (hasSummaryText) + strArray[0] = summaryText; + if (ptidCount > 0) + { + int currentIndex = (hasSummaryText ? 1 : 0); + for (String s : summary.getParticipantIds()) + strArray[currentIndex++] = s; + } + return strArray; + } + + @Override + protected String getCellHtml(Visit visit, SummaryByVisitType summary) + { + if (summary == null || summary.getVialCount() == null) + return " "; + ActionURL link = PageFlowUtil.urlProvider(SpecimenUrls.class).getSpecimensURL(_container, true); + link = updateURLFilterParameter(link, "SpecimenDetail.Visit/SequenceNumMin", visit.getSequenceNumMinDouble()); + + link = updateURLFilterParameter(link, "SpecimenDetail.PrimaryType/Description", summary.getPrimaryType()); + SpecimenTypeLevel level = getTypeLevelEnum(); + if (level == SpecimenTypeLevel.Derivative || level == SpecimenTypeLevel.Additive) + link = updateURLFilterParameter(link, "SpecimenDetail.DerivativeType/Description", summary.getDerivative()); + if (level == SpecimenTypeLevel.Additive) + link = updateURLFilterParameter(link, "SpecimenDetail.AdditiveType/Description", summary.getAdditive()); + + String linkHtml = link.getLocalURIString(); + if (_filter != null) + linkHtml += "&" + getFilterQueryString(visit, summary); + String summaryString = getCellSummaryText(summary); + StringBuilder cellHtml = new StringBuilder(); + if (summaryString.length() > 0) + { + cellHtml.append(""); + cellHtml.append(summaryString).append(""); + } + + if (isViewPtidList()) + { + if (cellHtml.length() > 0) + cellHtml.append("
"); + if (summary.getParticipantIds() != null) + { + for (Iterator it = summary.getParticipantIds().iterator(); it.hasNext();) + { + String participantId = it.next(); + ActionURL url = new ActionURL(SpecimenReportActions.TypeParticipantReportAction.class, _container); + url.addParameter("participantId", participantId); + url.addParameter(SpecimenVisitReportParameters.PARAMS.typeLevel, getTypeLevelEnum().name()); + url.addParameter(SpecimenVisitReportParameters.PARAMS.statusFilterName, getStatusFilterName()); + url.addParameter(SpecimenVisitReportParameters.PARAMS.viewVialCount, isViewVialCount()); + url.addParameter(SpecimenVisitReportParameters.PARAMS.viewParticipantCount, isViewParticipantCount()); + url.addParameter(SpecimenVisitReportParameters.PARAMS.viewVolume, isViewVolume()); + if (getBaseCustomViewName() != null && getBaseCustomViewName().length() > 0 && + !SpecimenVisitReportParameters.DEFAULT_VIEW_ID.equals(getBaseCustomViewName())) + { + url.addParameter(SpecimenVisitReportParameters.PARAMS.baseCustomViewName, getBaseCustomViewName()); + } + cellHtml.append(""); + cellHtml.append(PageFlowUtil.filter(DemoMode.id(participantId, getContainer(), getUser()))); + cellHtml.append(""); + if (it.hasNext()) + cellHtml.append("
"); + } + } + } + return cellHtml.toString(); + } +} diff --git a/specimen/src/org/labkey/specimen/report/SpecimenVisitReport.java b/specimen/src/org/labkey/specimen/report/SpecimenVisitReport.java new file mode 100644 index 00000000..178a2898 --- /dev/null +++ b/specimen/src/org/labkey/specimen/report/SpecimenVisitReport.java @@ -0,0 +1,427 @@ +/* + * 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.specimen.report; + +import org.labkey.api.data.Container; +import org.labkey.api.data.SimpleFilter; +import org.labkey.api.query.CustomView; +import org.labkey.api.query.FieldKey; +import org.labkey.api.query.QueryDefinition; +import org.labkey.api.query.QueryService; +import org.labkey.api.query.UserSchema; +import org.labkey.api.security.User; +import org.labkey.api.specimen.SpecimenQuerySchema; +import org.labkey.api.specimen.query.SpecimenQueryView; +import org.labkey.api.study.CohortFilter; +import org.labkey.api.study.Study; +import org.labkey.api.study.StudyService; +import org.labkey.api.study.Visit; +import org.labkey.api.study.model.ParticipantGroup; +import org.labkey.api.study.model.ParticipantGroupService; +import org.labkey.api.util.PageFlowUtil; +import org.labkey.api.view.ActionURL; +import org.labkey.api.view.NotFoundException; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * User: brittp + * Created: Jan 14, 2008 10:14:50 AM + */ +public abstract class SpecimenVisitReport +{ + private final List _visits; + private final SpecimenVisitReportParameters _parameters; + private final boolean _viewParticipantCount; + private final boolean _viewVolume; + private final boolean _viewPtidList; + private final boolean _viewVialCount; + private final String _title; + private final Map _nonEmptyColumns = new HashMap<>(); + + protected final Container _container; + protected final SimpleFilter _filter; + + private Collection _rows; + private List _nonEmptyVisits; + + public SpecimenVisitReport(String titlePrefix, List visits, SimpleFilter filter, SpecimenVisitReportParameters parameters) + { + _visits = visits; + _filter = filter; + _parameters = parameters; + _container = parameters.getContainer(); + _viewParticipantCount = parameters.isViewParticipantCount(); + _viewVolume = parameters.isViewVolume(); + _viewPtidList = parameters.isViewPtidList(); + // We'll show the vial count if it's explicitly requested or if nothing else has been selected: + _viewVialCount = parameters.isViewVialCount() || (!_viewParticipantCount && !_viewVolume && !_viewPtidList); + _title = titlePrefix + getTitleSuffix(); + } + + public Collection getRows() + { + if (_rows == null) + _rows = createRows(); + return _rows; + } + + protected abstract Collection createRows(); + + public String getTitle() + { + return _title; + } + + protected ActionURL updateURLFilterParameter(ActionURL url, String columnBase, Object value) + { + url.deleteParameter(columnBase + "~eq"); + url.deleteParameter(columnBase + "~isblank"); + if (value == null) + url.addParameter(columnBase + "~isblank", null); + else + url.addParameter(columnBase + "~eq", value.toString()); + return url; + } + + public List getVisits() + { + // ensure rows and non-empty columns have been generated + getRows(); + if (!_parameters.isHideEmptyColumns()) + return _visits; + else + { + if (_nonEmptyVisits == null) + { + List visits = new ArrayList<>(); + for (Visit visit : _visits) + { + if (_nonEmptyColumns.containsKey(visit.getId())) + visits.add(visit); + } + _nonEmptyVisits = new ArrayList<>(visits); + } + return _nonEmptyVisits; + } + } + + protected void setVisitAsNonEmpty(Integer visit) + { + if (!_nonEmptyColumns.containsKey(visit)) + _nonEmptyColumns.put(visit, visit); + } + + protected abstract String getCellHtml(Visit visit, CELLDATA summary); + + protected abstract String[] getCellExcelText(Visit visit, CELLDATA summary); + + public boolean isNumericData() + { + int valuesDisplayed =(_parameters.isViewVialCount() ? 1 : 0) + + (_parameters.isViewParticipantCount() ? 1 : 0) + + (_parameters.isViewVolume() ? 1 : 0); + return (valuesDisplayed == 1 && !_parameters.isViewPtidList()); + } + + private String getTitleSuffix() + { + StringBuilder suffixBuilder = new StringBuilder(); + if (isViewVialCount()) + suffixBuilder.append(" (Vial Count"); + if (isViewVolume()) + { + suffixBuilder.append(suffixBuilder.length() > 0 ? "/" : " ("); + suffixBuilder.append("Total Volume"); + } + if (isViewParticipantCount()) + { + suffixBuilder.append(suffixBuilder.length() > 0 ? "/" : " ("); + suffixBuilder.append(PageFlowUtil.filter(StudyService.get().getSubjectNounSingular(getContainer()))).append(" Count"); + } + if (isViewPtidList()) + { + suffixBuilder.append(suffixBuilder.length() > 0 ? ", " : " ("); + suffixBuilder.append(PageFlowUtil.filter(StudyService.get().getSubjectColumnName(getContainer()))).append(" Group"); + } + suffixBuilder.append(")"); + return suffixBuilder.toString(); + } + + private SimpleFilter geBaseViewFilter() + { + SimpleFilter fullFilter = new SimpleFilter(); + fullFilter.addAllClauses(_filter); + // This is a terrible hack to deal with the fact that the some SpecimenDetail columns have been aliased + // in the query view. As a result, we need to use the view's column name for filtering + // at the database layer, and then map this column name for use in a query view filter parameter: + fullFilter = replaceFilterParameterName(fullFilter, "PrimaryTypeId", FieldKey.fromParts("PrimaryType", "RowId").toString()); + fullFilter = replaceFilterParameterName(fullFilter, "DerivativeTypeId", FieldKey.fromParts("DerivativeType", "RowId").toString()); + fullFilter = replaceFilterParameterName(fullFilter, "AdditiveTypeId", FieldKey.fromParts("AdditiveType", "RowId").toString()); + fullFilter = replaceFilterParameterName(fullFilter, "ptid", StudyService.get().getSubjectColumnName(getContainer())); + return fullFilter; + } + + protected void addCohortURLFilter(Study study, ActionURL url) + { + if (_parameters.getCohortFilter() != null) + _parameters.getCohortFilter().addURLParameters(study, url, null); + } + + protected String getFilterQueryString(Visit visit, CELLDATA summary) + { + ActionURL url = new ActionURL(); + if (_parameters.getBaseCustomViewName() != null && _parameters.getBaseCustomViewName().length() > 0) + { + if (!SpecimenVisitReportParameters.DEFAULT_VIEW_ID.equals(_parameters.getBaseCustomViewName())) + url.addParameter("SpecimenDetail.viewName", _parameters.getBaseCustomViewName()); + } + else + url.addParameter("SpecimenDetail.ignoreFilter", "1"); + + + Study study = StudyService.get().getStudy(visit.getContainer()); + if (null != study) + addCohortURLFilter(study, url); + + if (_parameters.getParticipantGroupFilter() >= 0) + { + ParticipantGroup filterGroup = ParticipantGroupService.get().getParticipantGroup(getContainer(), getUser(), _parameters.getParticipantGroupFilter()); + if (filterGroup != null) + filterGroup.addURLFilter(url, getContainer(), "SpecimenDetail"); + } + + switch (_parameters.getStatusFilter()) + { + case ALL: + case AVAILABLE: + case UNAVAILABLE: + case REQUESTED_INPROCESS: + case NOT_REQUESTED_INPROCESS: + // We don't need to add any special URL parameter here: these filters are just based on columns + // in SpecimenDetail, so the query string should be populated correctly due to geBaseViewFilter().toQueryString(). + break; + case NOT_REQUESTED_COMPLETE: + url.addParameter(SpecimenQueryView.PARAMS.showNotCompleteRequestedOnly, "true"); + break; + case REQUESTED_COMPLETE: + url.addParameter(SpecimenQueryView.PARAMS.showCompleteRequestedOnly, "true"); + break; + } + + String baseParams = geBaseViewFilter().toQueryString("SpecimenDetail"); + if (baseParams != null && baseParams.length() > 0) + return baseParams + "&" + url.getQueryString(); + else + return url.getQueryString(); + } + + public int getLabelDepth() + { + Collection rows = getRows(); + if (rows.isEmpty()) + return 0; + return rows.iterator().next().getTitleHierarchy().length; + } + + private static SpecimenReportTitle[] getSpecimenReportTitle(String[] stringHierarchy) + { + SpecimenReportTitle[] titleHierarchy = new SpecimenReportTitle[stringHierarchy.length]; + + for (int i = 0; i < titleHierarchy.length; i++) + titleHierarchy[i] = new SpecimenReportTitle(stringHierarchy[i]); + + return titleHierarchy; + } + + public class Row + { + private final SpecimenReportTitle[] _titleHierarchy; + private final Map _visitData = new HashMap<>(); + + public Row(String[] stringHierarchy) + { + this(getSpecimenReportTitle(stringHierarchy)); + } + + public Row(SpecimenReportTitle[] titleHierarchy) + { + _titleHierarchy = titleHierarchy; + } + + public String getCellHtml(Visit visit) + { + CELLDATA summary = _visitData.get(visit.getId()); + return SpecimenVisitReport.this.getCellHtml(visit, summary); + } + + public String[] getCellExcelText(Visit visit) + { + CELLDATA summary = _visitData.get(visit.getId()); + return SpecimenVisitReport.this.getCellExcelText(visit, summary); + } + + public int getMaxExcelRowHeight(List visits) + { + int max = 1; + for (Visit visit : visits) + { + int currentHeight = getCellExcelText(visit).length; + if (currentHeight > max) + max = currentHeight; + } + return max; + } + + public SpecimenReportTitle[] getTitleHierarchy() + { + return _titleHierarchy; + } + + public void add(CELLDATA summary) + { + _visitData.put(summary.getVisit(), summary); + } + } + + protected SimpleFilter replaceFilterParameterName(SimpleFilter filter, String oldKey, String newKey) + { + FieldKey oldFieldKey = FieldKey.fromString(oldKey); + for (SimpleFilter.FilterClause clause : filter.getClauses()) + { + if (clause.getFieldKeys().size() == 1 && oldFieldKey.equals(clause.getFieldKeys().get(0))) + { + if (clause.getParamVals().length > 1) + throw new UnsupportedOperationException("Only single filters are supported on column " + newKey); + filter.deleteConditions(oldFieldKey); + if (clause.getParamVals().length != 1) + throw new UnsupportedOperationException("Reports that provide custom SQL filters must override getFilterQueryString."); + filter.addCondition(newKey, clause.getParamVals()[0]); + return filter; + } + } + return filter; + } + + protected boolean hasContent(String[] strArray) + { + if (strArray == null || strArray.length == 0) + return false; + for (String str : strArray) + { + if (str != null && str.length() > 0) + return true; + } + return false; + } + + protected String buildCellHtml(Visit visit, CELLDATA summary, String linkHtml) + { + String[] summaryString = getCellExcelText(visit, summary); + StringBuilder cellHtml = new StringBuilder(); + if (hasContent(summaryString)) + { + if (linkHtml != null) + cellHtml.append(""); + boolean first = true; + for (String str : summaryString) + { + if (str != null && str.length() > 0) + { + if (!first) + cellHtml.append("
"); + first = false; + cellHtml.append(str); + } + } + if (linkHtml != null) + cellHtml.append("
"); + } + return cellHtml.toString(); + } + + protected Container getContainer() + { + return _parameters.getContainer(); + } + + protected User getUser() + { + return _parameters.getUser(); + } + + protected boolean isViewVialCount() + { + return _viewVialCount; + } + + protected boolean isViewParticipantCount() + { + return _viewParticipantCount; + } + + protected boolean isViewVolume() + { + return _viewVolume; + } + + protected boolean isViewPtidList() + { + return _viewPtidList; + } + + protected String getBaseCustomViewName() + { + return _parameters.getBaseCustomViewName(); + } + + protected SpecimenTypeLevel getTypeLevelEnum() + { + return _parameters.getTypeLevelEnum(); + } + + protected String getStatusFilterName() + { + return _parameters.getStatusFilterName(); + } + + protected CohortFilter getCohortFilter() + { + if (StudyService.get().showCohorts(_container, getUser())) + return _parameters.getCohortFilter(); + return null; + } + + protected CustomView getBaseCustomView() + { + if (_parameters.getBaseCustomViewName() == null) + return null; + UserSchema schema = SpecimenQuerySchema.get(StudyService.get().getStudy(_container), _parameters.getUser()); + QueryDefinition def = QueryService.get().createQueryDefForTable(schema, "SpecimenDetail"); + String customViewName = _parameters.getBaseCustomViewName(); + if (SpecimenVisitReportParameters.DEFAULT_VIEW_ID.equals(customViewName)) + customViewName = null; + CustomView view = def.getCustomView(_parameters.getUser(), _parameters.getViewContext().getRequest(), customViewName); + if (view == null) + throw new NotFoundException("Custom view " + _parameters.getBaseCustomViewName() + " was not found. It may have been deleted by another user."); + + return view; + } +} diff --git a/specimen/src/org/labkey/specimen/report/SpecimenVisitReportAction.java b/specimen/src/org/labkey/specimen/report/SpecimenVisitReportAction.java new file mode 100644 index 00000000..cbd660ca --- /dev/null +++ b/specimen/src/org/labkey/specimen/report/SpecimenVisitReportAction.java @@ -0,0 +1,60 @@ +package org.labkey.specimen.report; + +import org.labkey.api.action.SimpleViewAction; +import org.labkey.api.data.Container; +import org.labkey.api.view.ActionURL; +import org.labkey.api.view.JspView; +import org.labkey.api.view.NavTree; +import org.labkey.api.view.VBox; +import org.labkey.specimen.actions.ReportConfigurationBean; +import org.labkey.specimen.actions.SpecimenController2; +import org.labkey.specimen.actions.SpecimenController2.AutoReportListAction; +import org.springframework.validation.BindException; +import org.springframework.web.servlet.ModelAndView; + +public abstract class SpecimenVisitReportAction extends SimpleViewAction +{ + private FormType _form; + + public SpecimenVisitReportAction(Class beanClass) + { + super(beanClass); + } + + @Override + public ModelAndView getView(FormType specimenVisitReportForm, BindException errors) + { + _form = specimenVisitReportForm; + if (specimenVisitReportForm.isExcelExport()) + { + SpecimenReportExcelWriter writer = new SpecimenReportExcelWriter(specimenVisitReportForm); + writer.write(getViewContext().getResponse()); + return null; + } + else + { + JspView reportView = new JspView<>("/org/labkey/specimen/view/specimenVisitReport.jsp", specimenVisitReportForm); + reportView.setIsWebPart(false); + if (this.isPrint()) + { + return reportView; + } + else + { + // Need unique id only in webpart case + ReportConfigurationBean bean = new ReportConfigurationBean(specimenVisitReportForm, false, 0); + return new VBox(new JspView<>("/org/labkey/specimen/view/autoReportList.jsp", bean), reportView); + } + } + } + + @Override + public void addNavTrail(NavTree root) + { + Container c = getContainer(); + ActionURL overviewURL = new ActionURL(SpecimenController2.OverviewAction.class, c); + root.addChild("Specimen Overview", overviewURL); + root.addChild("Available Reports", new ActionURL(AutoReportListAction.class, c)); + root.addChild("Specimen Report: " + _form.getLabel()); + } +} diff --git a/specimen/src/org/labkey/specimen/report/SpecimenVisitReportParameters.java b/specimen/src/org/labkey/specimen/report/SpecimenVisitReportParameters.java new file mode 100644 index 00000000..f94232db --- /dev/null +++ b/specimen/src/org/labkey/specimen/report/SpecimenVisitReportParameters.java @@ -0,0 +1,544 @@ +/* + * 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.specimen.report; + +import org.labkey.api.data.SimpleFilter; +import org.labkey.api.jsp.taglib.AutoCompleteTextTag; +import org.labkey.api.query.CustomView; +import org.labkey.api.query.FieldKey; +import org.labkey.api.security.User; +import org.labkey.api.specimen.SpecimenSchema; +import org.labkey.api.specimen.location.LocationImpl; +import org.labkey.api.specimen.settings.SettingsManager; +import org.labkey.api.study.Cohort; +import org.labkey.api.study.CohortFilter; +import org.labkey.api.study.Study; +import org.labkey.api.study.StudyService; +import org.labkey.api.study.model.CohortService; +import org.labkey.api.study.model.ParticipantGroup; +import org.labkey.api.study.model.ParticipantGroupService; +import org.labkey.api.util.DemoMode; +import org.labkey.api.util.HtmlString; +import org.labkey.api.util.PageFlowUtil; +import org.labkey.api.util.Pair; +import org.labkey.api.util.SafeToRenderEnum; +import org.labkey.api.util.element.Option.OptionBuilder; +import org.labkey.api.util.element.Select; +import org.labkey.api.view.ActionURL; +import org.labkey.api.view.UnauthorizedException; +import org.labkey.api.view.ViewForm; +import org.labkey.specimen.actions.SpecimenController2.CompleteSpecimenAction; + +import java.io.IOException; +import java.io.StringWriter; +import java.io.Writer; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static org.labkey.api.util.HtmlString.unsafe; + +/** + * User: brittp + * Created: Jan 14, 2008 12:26:17 PM + */ +public abstract class SpecimenVisitReportParameters extends ViewForm +{ + public static final String DEFAULT_VIEW_ID = "~default~"; + + public enum PARAMS + { + statusFilterName, + viewVialCount, + viewParticipantCount, + viewVolume, + baseCustomViewName, + viewPtidList, + typeLevel, + hideEmptyColumns + } + + public enum Status implements SafeToRenderEnum + { + ALL("All vials"), + AVAILABLE("Available vials"), + UNAVAILABLE("Unavailable vials"), + REQUESTED_INPROCESS("Vials in any request (including in-process)"), + NOT_REQUESTED_INPROCESS("Vials not in in any request"), + REQUESTED_COMPLETE("Vials in completed requests"), + NOT_REQUESTED_COMPLETE("Vials not in completed requests"); + + private final String _caption; + + Status(String caption) + { + _caption = caption; + } + + public String getCaption() + { + return _caption; + } + } + + private CohortFilter _cohortFilter; + private Status _statusFilter = Status.ALL; + private String _baseCustomViewName; + private boolean _viewVialCount = false; + private boolean _viewParticipantCount = false; + private boolean _viewVolume = false; + private boolean _viewPtidList = false; + private boolean _hideEmptyColumns; + private boolean _excelExport; + private int _participantGroupFilter = -1; + private List _reports; + private SpecimenTypeLevel _typeLevel = SpecimenTypeLevel.Derivative; + + public SpecimenVisitReportParameters() + { + _cohortFilter = CohortService.get().getFromURL(getViewContext().getContainer(), getViewContext().getUser(), getViewContext().getActionURL()); + } + + public String getTypeLevel() + { + return _typeLevel.name(); + } + + public void setTypeLevel(String typeLevel) + { + _typeLevel = SpecimenTypeLevel.valueOf(typeLevel); + } + + public SpecimenTypeLevel getTypeLevelEnum() + { + return _typeLevel; + } + + public CohortFilter getCohortFilter() + { + return _cohortFilter; + } + + public Cohort getCohort() + { + return _cohortFilter != null ? _cohortFilter.getCohort(getContainer(), getUser()) : null; + } + + public void setCohortFilter(CohortFilter cohortFilter) + { + if (StudyService.get().showCohorts(getContainer(), getUser())) + _cohortFilter = cohortFilter; + } + + public List> getAdditionalFormInputHtml(User user) + { + return Collections.emptyList(); + } + + public String getStatusFilterName() + { + return _statusFilter.name(); + } + + public void setStatusFilterName(String statusFilter) + { + _statusFilter = Status.valueOf(statusFilter); + } + + public Status getStatusFilter() + { + return _statusFilter; + } + + public boolean isViewVialCount() + { + return _viewVialCount; + } + + public void setViewVialCount(boolean viewVialCount) + { + _viewVialCount = viewVialCount; + } + + public boolean isViewParticipantCount() + { + return _viewParticipantCount; + } + + public void setViewParticipantCount(boolean viewParticipantCount) + { + _viewParticipantCount = viewParticipantCount; + } + + public boolean isViewVolume() + { + return _viewVolume; + } + + public void setViewVolume(boolean viewVolume) + { + _viewVolume = viewVolume; + } + + public boolean isViewPtidList() + { + return _viewPtidList; + } + + public void setViewPtidList(boolean viewPtidList) + { + _viewPtidList = viewPtidList; + } + + public boolean isHideEmptyColumns() + { + return _hideEmptyColumns; + } + + public void setHideEmptyColumns(boolean hideEmptyColumns) + { + _hideEmptyColumns = hideEmptyColumns; + } + + public boolean isExcelExport() + { + return _excelExport; + } + + public void setExcelExport(boolean excelExport) + { + _excelExport = excelExport; + } + + public String getBaseCustomViewName() + { + return _baseCustomViewName; + } + + public void setBaseCustomViewName(String baseCustomViewName) + { + _baseCustomViewName = baseCustomViewName; + } + + protected void addBaseFilters(SimpleFilter filter) + { + if (allowsAvailabilityFilter() && getStatusFilter() != null) + addAvailabilityFilter(filter, getStatusFilter()); + + if (allowsCohortFilter() && getCohortFilter() != null) + addCohortFilter(filter, getCohortFilter()); + + if (allowsParticipantGroupFilter() && getParticipantGroupFilter() >= 0) + addParticipantGroupFilter(filter, getParticipantGroupFilter()); + } + + public static final String COMPLETED_REQUESTS_FILTER_SQL = + "SELECT SpecimenGlobalUniqueId FROM study.SampleRequestSpecimen\n" + + "\tJOIN study.SampleRequest ON study.SampleRequestSpecimen.SampleRequestId = study.SampleRequest.RowId\n" + + "\tJOIN study.SampleRequestStatus ON study.SampleRequest.StatusId = study.SampleRequestStatus.RowId\n" + + "\tWHERE study.SampleRequestStatus.SpecimensLocked = ? AND study.SampleRequestStatus.FinalState = ?\n" + + "\tAND study.SampleRequest.Container = ?"; + + protected void addAvailabilityFilter(SimpleFilter filter, Status status) + { + switch (status) + { + case ALL: + break; + case AVAILABLE: + filter.addCondition(FieldKey.fromParts("Available"), Boolean.TRUE); + break; + case UNAVAILABLE: + filter.addCondition(FieldKey.fromParts("Available"), Boolean.FALSE); + break; + case REQUESTED_INPROCESS: + filter.addCondition(FieldKey.fromParts("LockedInRequest"), Boolean.TRUE); + break; + case NOT_REQUESTED_INPROCESS: + filter.addCondition(FieldKey.fromParts("LockedInRequest"), Boolean.FALSE); + break; + case REQUESTED_COMPLETE: + filter.addWhereClause("GlobalUniqueId IN (\n" + COMPLETED_REQUESTS_FILTER_SQL + ")", + new Object[] { Boolean.TRUE, Boolean.TRUE, getContainer().getId()}); + break; + case NOT_REQUESTED_COMPLETE: + filter.addWhereClause("GlobalUniqueId NOT IN (\n" + COMPLETED_REQUESTS_FILTER_SQL + ")", + new Object[] { Boolean.TRUE, Boolean.TRUE, getContainer().getId()}); + break; + } + } + + protected void addParticipantGroupFilter(SimpleFilter filter, int ptidListId) + { + ParticipantGroup group = ParticipantGroupService.get().getParticipantGroup(getContainer(), getUser(), ptidListId); + if (group != null) + { + StringBuilder sql = new StringBuilder(); + sql.append("(").append(StudyService.get().getSubjectColumnName(getContainer())).append(" IN (SELECT "); + sql.append("ParticipantId FROM ").append(SpecimenSchema.get().getTableInfoParticipantGroupMap()).append(" WHERE GroupId = ?))"); + filter.addWhereClause(sql.toString(), new Object[] { ptidListId }); + } + } + + protected void addCohortFilter(SimpleFilter filter, CohortFilter cohortFilter) + { + if (!StudyService.get().showCohorts(getContainer(), getUser())) + throw new UnauthorizedException("User does not have permission to view cohort information"); + + if (cohortFilter == CohortService.get().getUnassignedCohortFilter()) + { + filter.addWhereClause("(" + StudyService.get().getSubjectColumnName(getContainer()) + " IN\n" + + "(SELECT ParticipantId FROM study.participant WHERE CurrentCohortId IS NULL AND Container = ?)" + + " OR (" + StudyService.get().getSubjectColumnName(getContainer()) + + " NOT IN (SELECT ParticipantId FROM study.participant WHERE Container = ?)))", + new Object[] { getContainer().getId(), getContainer().getId()}); + } + else if (cohortFilter != null) + { + Cohort cohort = cohortFilter.getCohort(getContainer(),getUser()); + int cohortId = null==cohort ? -1 : cohort.getRowId(); + + switch (cohortFilter.getType()) + { + case DATA_COLLECTION: + filter.addWhereClause("CollectionCohort = ? AND Container = ?", + new Object[] { cohortId, getContainer().getId()} ); + break; + case PTID_CURRENT: + filter.addWhereClause(StudyService.get().getSubjectColumnName(getContainer()) + " IN\n" + + "(SELECT ParticipantId FROM study.participant WHERE CurrentCohortId = ? AND Container = ?)", + new Object[] { cohortId, getContainer().getId()}); + break; + case PTID_INITIAL: + filter.addWhereClause(StudyService.get().getSubjectColumnName(getContainer()) + " IN\n" + + "(SELECT ParticipantId FROM study.participant WHERE InitialCohortId = ? AND Container = ?)", + new Object[] { cohortId, getContainer().getId()}); + break; + } + } + } + + protected Pair getEnrollmentSitePicker(String inputName, Set locations, Integer selectedSiteId) + { + Select.SelectBuilder sb = new Select.SelectBuilder() + .name(inputName) + .addOption(new OptionBuilder() + .value("") + .label("All enrollment locations") + .build()); + + for (LocationImpl location : locations) + { + String label = "Unassigned enrollment location"; + String value = "-1"; + int currentSelectedSite = -1; + + if (location != null) + { + label = location.getLabel(); + value = Integer.toString(location.getRowId()); + currentSelectedSite = location.getRowId(); + } + + boolean selected = selectedSiteId != null && selectedSiteId == currentSelectedSite; + OptionBuilder ob = new OptionBuilder() + .value(value) + .label(label) + .selected(selected); + + sb.addOption(ob.build()); + } + + return new Pair<>("Enrollment site", unsafe(sb.toString())); + } + + public HtmlString getCustomViewPicker(Map specimenDetailViews) + { + Select.SelectBuilder sb = new Select.SelectBuilder() + .name("baseCustomViewName") + .addOption(new OptionBuilder() + .value("") + .label("Base report on all vials") + .build()); + + for (Map.Entry viewEntry : specimenDetailViews.entrySet()) + { + OptionBuilder ob = new OptionBuilder(); + String name = viewEntry.getKey(); + CustomView view = viewEntry.getValue(); + + if (view.getFilterAndSort() != null) + { + String viewName = DEFAULT_VIEW_ID; + String label = "Base on default view (filtered)"; + + if (name != null) + { + viewName = view.getName(); + label = "Base on view: " + view.getName(); + } + + boolean selected = _baseCustomViewName != null && _baseCustomViewName.equals(viewName); + sb.addOption(ob + .value(viewName) + .label(label) + .selected(selected) + .build()); + } + } + + return unsafe(sb.toString()); + } + + private String _allString = null; + protected String getAllString() + { + if (_allString == null) + _allString = "All " + PageFlowUtil.filter(StudyService.get().getSubjectNounPlural(getContainer())) + " (Large Report)"; + return _allString; + } + + public boolean isAllSubjectsOption(String subject) + { + if (subject == null) + return false; + String allString = getAllString().toLowerCase(); + subject = subject.toLowerCase(); + if (subject.length() > allString.length()) + return false; + return allString.startsWith(subject); + } + + protected Pair getParticipantPicker(String inputName, String selectedParticipantId) + { + Study study = StudyService.get().getStudy(getContainer()); + Select.SelectBuilder builder = new Select.SelectBuilder(); + + String allString = getAllString(); + Collection participantIds = StudyService.get().getParticipantIds(study, getUser()); + HtmlString participantPickerValues; + if (participantIds.size() <= 200) + { + // select the previously selected option or the first non-all option. We don't want to select 'all participants' + // by default, since these reports are extremely expensive to generate. + builder.name(inputName) + .addOption(new OptionBuilder() + .value(allString) + .label(allString) + .selected(isAllSubjectsOption(selectedParticipantId)) + .build()); + + boolean first = true; + for (String participantId : participantIds) + { + boolean isSelected = (selectedParticipantId != null && selectedParticipantId.equals(participantId)) || + (selectedParticipantId == null && first); + first = false; + + builder.addOption(new OptionBuilder() + .value(participantId) + .label(DemoMode.id(participantId, getContainer(), getUser())) + .selected(isSelected) + .build()); + } + participantPickerValues = unsafe(builder.toString()); + } + else + { + ActionURL completionUrl = new ActionURL(CompleteSpecimenAction.class, getContainer()).addParameter("type", "ParticipantId"); + String initValue = selectedParticipantId != null ? selectedParticipantId : participantIds.iterator().next(); + + StringWriter writer = new StringWriter(); + AutoCompleteTextTag tag = new AutoCompleteTextTag() + { + @Override + protected Writer getWriter() + { + return writer; + } + }; + tag.setUrl(completionUrl); + tag.setName(inputName); + tag.setValue(initValue); + + try + { + tag.doTag(); + } + catch (IOException e) + { + throw new RuntimeException(e); + } + + participantPickerValues = unsafe(writer.toString()); + } + + return new Pair<>(StudyService.get().getSubjectColumnName(getContainer()), participantPickerValues); + } + + protected abstract List createReports(); + + public abstract String getLabel(); + + /** Internal, stable, unique name used to identify this report type in webpart properties */ + public abstract String getReportType(); + + public boolean allowsCohortFilter() + { + return StudyService.get().showCohorts(getContainer(), getUser()); + } + + public boolean allowsAvailabilityFilter() + { + return SettingsManager.get().getRepositorySettings(getContainer()).isEnableRequests(); + } + + public boolean allowsParticipantAggregates() + { + return true; + } + + public boolean allowsCustomViewFilter() + { + return true; + } + + public boolean allowsParticipantGroupFilter() + { + return true; + } + + public int getParticipantGroupFilter() + { + return _participantGroupFilter; + } + + public void setParticipantGroupFilter(int participantGroupFilter) + { + _participantGroupFilter = participantGroupFilter; + } + + public abstract Class getAction(); + + public List getReports() + { + if (_reports == null) + _reports = createReports(); + return _reports; + } +} diff --git a/specimen/src/org/labkey/specimen/report/SummaryByVisitParticipant.java b/specimen/src/org/labkey/specimen/report/SummaryByVisitParticipant.java new file mode 100644 index 00000000..61d07def --- /dev/null +++ b/specimen/src/org/labkey/specimen/report/SummaryByVisitParticipant.java @@ -0,0 +1,27 @@ +package org.labkey.specimen.report; + +public class SummaryByVisitParticipant extends SpecimenCountSummary +{ + private String _participantId; + private String _cohort; + + public String getParticipantId() + { + return _participantId; + } + + public void setParticipantId(String participantId) + { + _participantId = participantId; + } + + public String getCohort() + { + return _cohort; + } + + public void setCohort(String cohort) + { + _cohort = cohort; + } +} diff --git a/specimen/src/org/labkey/specimen/report/SummaryByVisitType.java b/specimen/src/org/labkey/specimen/report/SummaryByVisitType.java new file mode 100644 index 00000000..7c0be688 --- /dev/null +++ b/specimen/src/org/labkey/specimen/report/SummaryByVisitType.java @@ -0,0 +1,62 @@ +package org.labkey.specimen.report; + +import java.util.Set; + +public class SummaryByVisitType extends SpecimenCountSummary +{ + private String _primaryType; + private String _derivative; + private String _additive; + private Long _participantCount; + private Set _participantIds; + + public String getPrimaryType() + { + return _primaryType; + } + + public void setPrimaryType(String primaryType) + { + _primaryType = primaryType; + } + + public String getDerivative() + { + return _derivative; + } + + public void setDerivative(String derivative) + { + _derivative = derivative; + } + + public Long getParticipantCount() + { + return _participantCount; + } + + public void setParticipantCount(Long participantCount) + { + _participantCount = participantCount; + } + + public Set getParticipantIds() + { + return _participantIds; + } + + public void setParticipantIds(Set participantIds) + { + _participantIds = participantIds; + } + + public String getAdditive() + { + return _additive; + } + + public void setAdditive(String additive) + { + _additive = additive; + } +} diff --git a/specimen/src/org/labkey/specimen/report/participant/ParticipantSiteReport.java b/specimen/src/org/labkey/specimen/report/participant/ParticipantSiteReport.java new file mode 100644 index 00000000..670d35d7 --- /dev/null +++ b/specimen/src/org/labkey/specimen/report/participant/ParticipantSiteReport.java @@ -0,0 +1,45 @@ +/* + * 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.specimen.report.participant; + +import org.labkey.api.data.SimpleFilter; +import org.labkey.api.study.Visit; +import org.labkey.specimen.report.SpecimenVisitReportParameters; + +import java.util.List; + +/** + * User: brittp + * Created: Feb 4, 2008 3:48:04 PM + */ +public class ParticipantSiteReport extends ParticipantVisitReport +{ + public ParticipantSiteReport(String title, List visits, SimpleFilter filter, SpecimenVisitReportParameters parameters) + { + super(title, visits, filter, parameters); + } + + @Override + protected SimpleFilter replaceFilterParameterName(SimpleFilter filter, String oldKey, String newKey) + { + // this report adds a strange filter to 'ptid' that we want to just drop when it comes + // time to generate our link URLs: + if (!"ptid".equals(oldKey)) + return super.replaceFilterParameterName(filter, oldKey, newKey); + else + return filter; + } +} diff --git a/specimen/src/org/labkey/specimen/report/participant/ParticipantSiteReportFactory.java b/specimen/src/org/labkey/specimen/report/participant/ParticipantSiteReportFactory.java new file mode 100644 index 00000000..6545c6d1 --- /dev/null +++ b/specimen/src/org/labkey/specimen/report/participant/ParticipantSiteReportFactory.java @@ -0,0 +1,131 @@ +/* + * 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.specimen.report.participant; + +import org.labkey.api.data.SimpleFilter; +import org.labkey.api.query.FieldKey; +import org.labkey.api.security.User; +import org.labkey.api.specimen.SpecimenManager; +import org.labkey.api.specimen.location.LocationImpl; +import org.labkey.api.specimen.location.LocationManager; +import org.labkey.specimen.report.SpecimenVisitReport; +import org.labkey.specimen.report.SpecimenVisitReportAction; +import org.labkey.specimen.report.SpecimenVisitReportParameters; +import org.labkey.specimen.actions.SpecimenReportActions; +import org.labkey.api.study.StudyService; +import org.labkey.api.study.Visit; +import org.labkey.api.util.HtmlString; +import org.labkey.api.util.Pair; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Set; + +/** + * User: brittp + * Created: Jan 30, 2008 3:40:07 PM + */ +public class ParticipantSiteReportFactory extends SpecimenVisitReportParameters +{ + private Integer _enrollmentSiteId; + + @Override + protected List createReports() + { + List visits = SpecimenManager.get().getVisitsWithSpecimens(getContainer(), getUser(), getCohort()); + List reports = new ArrayList<>(); + Set enrollmentLocations; + if (_enrollmentSiteId == null) + { + enrollmentLocations = SpecimenManager.get().getEnrollmentSitesWithSpecimens(getContainer(), getUser()); + // add null to the set so we can search for ptid without an enrollment site: + enrollmentLocations.add(null); + } + else if (_enrollmentSiteId == -1) + { + enrollmentLocations = Collections.singleton(null); + } + else + { + enrollmentLocations = Collections.singleton(LocationManager.get().getLocation(getContainer(), _enrollmentSiteId)); + } + + for (LocationImpl location : enrollmentLocations) + { + String label = location != null ? location.getLabel() : "[No enrollment location assigned]"; + SimpleFilter filter = new SimpleFilter(); + addBaseFilters(filter); + if (location != null) + { + filter.addWhereClause("" + StudyService.get().getSubjectColumnName(getContainer()) + " IN (SELECT ParticipantId FROM study.Participant " + + "WHERE EnrollmentSiteId = ? AND Container = ?)", new Object[] { location.getRowId(), getContainer().getId() }, FieldKey.fromParts(StudyService.get().getSubjectColumnName(getContainer()))); + } + else + { + filter.addWhereClause(StudyService.get().getSubjectColumnName(getContainer()) + " IN (SELECT ParticipantId FROM study.Participant " + + "WHERE EnrollmentSiteId IS NULL AND Container = ?)", new Object[] { getContainer().getId() }, FieldKey.fromParts(StudyService.get().getSubjectColumnName(getContainer()))); + } + reports.add(new ParticipantSiteReport(label, visits, filter, this)); + } + return reports; + } + + @Override + public boolean allowsParticipantAggregates() + { + return false; + } + + @Override + public List> getAdditionalFormInputHtml(User user) + { + List> inputs = new ArrayList<>(super.getAdditionalFormInputHtml(user)); + Set locations = SpecimenManager.get().getEnrollmentSitesWithSpecimens(getContainer(), getUser()); + // add null to the set so we can search for ptid without an enrollment site: + locations.add(null); + inputs.add(getEnrollmentSitePicker("enrollmentSiteId", locations, _enrollmentSiteId)); + return inputs; + } + + public Integer getEnrollmentSiteId() + { + return _enrollmentSiteId; + } + + public void setEnrollmentSiteId(Integer enrollmentSiteId) + { + _enrollmentSiteId = enrollmentSiteId; + } + + @Override + public String getLabel() + { + return StudyService.get().getSubjectNounSingular(getContainer()) + " by Enrollment Location"; + } + + @Override + public String getReportType() + { + return "ParticipantByEnrollmentLocation"; + } + + @Override + public Class getAction() + { + return SpecimenReportActions.ParticipantSiteReportAction.class; + } +} diff --git a/specimen/src/org/labkey/specimen/report/participant/ParticipantSummaryReportFactory.java b/specimen/src/org/labkey/specimen/report/participant/ParticipantSummaryReportFactory.java new file mode 100644 index 00000000..7315d2fa --- /dev/null +++ b/specimen/src/org/labkey/specimen/report/participant/ParticipantSummaryReportFactory.java @@ -0,0 +1,69 @@ +/* + * 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.specimen.report.participant; + +import org.labkey.api.data.SimpleFilter; +import org.labkey.api.specimen.SpecimenManager; +import org.labkey.specimen.report.SpecimenVisitReport; +import org.labkey.specimen.report.SpecimenVisitReportAction; +import org.labkey.specimen.report.SpecimenVisitReportParameters; +import org.labkey.specimen.actions.SpecimenReportActions; +import org.labkey.api.study.StudyService; +import org.labkey.api.study.Visit; + +import java.util.Collections; +import java.util.List; + +/** + * User: brittp + * Created: Jan 29, 2008 5:56:06 PM + */ +public class ParticipantSummaryReportFactory extends SpecimenVisitReportParameters +{ + @Override + protected List createReports() + { + List visits = SpecimenManager.get().getVisitsWithSpecimens(getContainer(), getUser(), getCohort()); + SimpleFilter filter = new SimpleFilter(); + addBaseFilters(filter); + ParticipantVisitReport report = new ParticipantVisitReport("Summary", visits, filter, this); + return Collections.singletonList(report); + } + + @Override + public String getLabel() + { + return StudyService.get().getSubjectNounSingular(getContainer()) + " Summary"; + } + + @Override + public String getReportType() + { + return "ParticipantSummary"; + } + + @Override + public boolean allowsParticipantAggregates() + { + return false; + } + + @Override + public Class getAction() + { + return SpecimenReportActions.ParticipantSummaryReportAction.class; + } +} diff --git a/specimen/src/org/labkey/specimen/report/participant/ParticipantTypeReportFactory.java b/specimen/src/org/labkey/specimen/report/participant/ParticipantTypeReportFactory.java new file mode 100644 index 00000000..d3170b07 --- /dev/null +++ b/specimen/src/org/labkey/specimen/report/participant/ParticipantTypeReportFactory.java @@ -0,0 +1,218 @@ +/* + * 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.specimen.report.participant; + +import org.labkey.api.data.CompareType; +import org.labkey.api.data.SimpleFilter; +import org.labkey.api.security.User; +import org.labkey.api.specimen.SpecimenManager; +import org.labkey.api.specimen.SpecimenManagerNew; +import org.labkey.api.specimen.model.SpecimenTypeSummary; +import org.labkey.specimen.report.SpecimenVisitReport; +import org.labkey.specimen.report.SpecimenVisitReportAction; +import org.labkey.specimen.report.SpecimenVisitReportParameters; +import org.labkey.specimen.actions.SpecimenReportActions; +import org.labkey.api.study.StudyService; +import org.labkey.api.study.Visit; +import org.labkey.api.util.HtmlString; +import org.labkey.api.util.Pair; +import org.labkey.api.util.element.Option; +import org.labkey.api.util.element.Select; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import static org.labkey.api.util.HtmlString.unsafe; + +/** + * User: brittp + * Created: Jan 30, 2008 3:40:07 PM + */ +public class ParticipantTypeReportFactory extends SpecimenVisitReportParameters +{ + private String _selectedType; + + @Override + protected List createReports() + { + List types = getSelectedTypes(); + List visits = SpecimenManager.get().getVisitsWithSpecimens(getContainer(), getUser(), getCohort()); + List reports = new ArrayList<>(); + for (SpecimenTypeSummary.TypeCount type : types) + { + String displayLabel = type.geDisplayLabel(); + SimpleFilter filter = new SimpleFilter(); + addBaseFilters(filter); + while (type != null) + { + if (type.getLabel() != null) + filter.addCondition(type.getSpecimenViewFilterColumn(), type.getLabel()); + else + filter.addCondition(type.getSpecimenViewFilterColumn(), type.getLabel(), CompareType.ISBLANK); + type = type.getParent(); + } + reports.add(new ParticipantVisitReport(displayLabel, visits, filter, this)); + } + return reports; + } + + @Override + public boolean allowsParticipantAggregates() + { + return false; + } + + @Override + public List> getAdditionalFormInputHtml(User user) + { + return Collections.singletonList(getSpecimenTypePicker()); + } + + private void appendOptions(List types, Select.SelectBuilder builder, String parentId, String selectedId, int indent) + { + for (SpecimenTypeSummary.TypeCount type : types) + { + String spacing = generateSpacing(indent); + String label = getLabel(type); + HtmlString encodedLabel = unsafe(spacing + HtmlString.of(label)); + + String id = parentId != null ? parentId + TYPE_COMPONENT_SEPARATOR + label : label; + builder.addOption(new Option.OptionBuilder() + .value(id) + .label(encodedLabel) + .selected(id.equals(selectedId)) + .build()); + + appendOptions(type.getChildren(), builder, id, selectedId, indent + 1); + } + } + + private String generateSpacing(int indent) + { + String spacing = ""; + for (int i = 0; i < indent * 5; i++) + { + spacing = spacing.concat(HtmlString.NBSP.toString()); + } + return spacing; + } + + public String getSelectedType() + { + return _selectedType; + } + + public void setSelectedType(String selectedType) + { + _selectedType = selectedType; + } + + private static final String ALL_PRIMARY_TYPES_FORM_VALUE = "allPrimary"; + private static final String ALL_DERIVATIVE_TYPES_FORM_VALUE = "allDerivative"; + private static final String ALL_ADDITIVE_TYPES_FORM_VALUE = "allAdditive"; + private static final String TYPE_COMPONENT_SEPARATOR = "~#~"; + + protected List getSelectedTypes() + { + SpecimenTypeSummary summary = SpecimenManagerNew.get().getSpecimenTypeSummary(getContainer(), getUser()); + if (_selectedType == null || _selectedType.equals(ALL_PRIMARY_TYPES_FORM_VALUE)) + return summary.getPrimaryTypes(); + if (_selectedType.equals(ALL_DERIVATIVE_TYPES_FORM_VALUE)) + return summary.getDerivatives(); + if (_selectedType.equals(ALL_ADDITIVE_TYPES_FORM_VALUE)) + return summary.getAdditives(); + String[] typeLabels = _selectedType.split(TYPE_COMPONENT_SEPARATOR); + + List types = summary.getPrimaryTypes(); + SpecimenTypeSummary.TypeCount selected = null; + for (String typeLabel : typeLabels) + { + selected = getTypeCountByLabel(types, typeLabel); + if (selected != null) + types = selected.getChildren(); + } + + // issue 13510: default to showing one report per primary type (if no type match found in list) + return selected == null ? summary.getPrimaryTypes() : Collections.singletonList(selected); + } + + private String getLabel(SpecimenTypeSummary.TypeCount type) + { + if (type.getLabel() == null || type.getLabel().length() == 0) + return "[unknown]"; + else + return type.getLabel(); + + } + + private SpecimenTypeSummary.TypeCount getTypeCountByLabel(List counts, String label) + { + boolean unknown = "[unknown]".equals(label); + for (SpecimenTypeSummary.TypeCount count : counts) + { + if (getLabel(count).equals(label) || (unknown && (count.getLabel() == null || count.getLabel().length() == 0))) + return count; + } + return null; + } + + protected Pair getSpecimenTypePicker() + { + Select.SelectBuilder select = new Select.SelectBuilder(); + SpecimenTypeSummary summary = SpecimenManagerNew.get().getSpecimenTypeSummary(getContainer(), getUser()); + select.name("selectedType"); + + select.addOption(new Option.OptionBuilder() + .value(ALL_PRIMARY_TYPES_FORM_VALUE) + .label("One report per primary type") + .selected(ALL_PRIMARY_TYPES_FORM_VALUE.equals(_selectedType)) + .build()); + + select.addOption(new Option.OptionBuilder() + .value(ALL_DERIVATIVE_TYPES_FORM_VALUE) + .label("One report per derivative type") + .selected(ALL_DERIVATIVE_TYPES_FORM_VALUE.equals(_selectedType)) + .build()); + + select.addOption(new Option.OptionBuilder() + .value(ALL_ADDITIVE_TYPES_FORM_VALUE) + .label("One report per additive type") + .selected(ALL_ADDITIVE_TYPES_FORM_VALUE.equals(_selectedType)) + .build()); + + appendOptions(summary.getPrimaryTypes(), select, null, _selectedType, 0); + return new Pair<>("Specimen type", unsafe(select.toString())); + } + + @Override + public String getLabel() + { + return StudyService.get().getSubjectNounSingular(getContainer()) + " by Specimen Type"; + } + + @Override + public String getReportType() + { + return "ParticipantByType"; + } + + @Override + public Class getAction() + { + return SpecimenReportActions.ParticipantTypeReportAction.class; + } +} diff --git a/specimen/src/org/labkey/specimen/report/participant/ParticipantVisitReport.java b/specimen/src/org/labkey/specimen/report/participant/ParticipantVisitReport.java new file mode 100644 index 00000000..12c7df16 --- /dev/null +++ b/specimen/src/org/labkey/specimen/report/participant/ParticipantVisitReport.java @@ -0,0 +1,124 @@ +/* + * 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.specimen.report.participant; + +import org.labkey.api.data.SimpleFilter; +import org.labkey.api.study.CohortFilter; +import org.labkey.api.study.SpecimenUrls; +import org.labkey.api.study.StudyService; +import org.labkey.api.study.Visit; +import org.labkey.api.util.DemoMode; +import org.labkey.api.util.PageFlowUtil; +import org.labkey.api.view.ActionURL; +import org.labkey.specimen.report.SpecimenReportManager; +import org.labkey.specimen.report.SpecimenReportTitle; +import org.labkey.specimen.report.SpecimenVisitReport; +import org.labkey.specimen.report.SpecimenVisitReportParameters; +import org.labkey.specimen.report.SummaryByVisitParticipant; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +/** + * User: brittp + * Created: Jan 29, 2008 4:52:26 PM + */ +public class ParticipantVisitReport extends SpecimenVisitReport +{ + private final boolean _showCohorts; + + public ParticipantVisitReport(String titlePrefix, List visits, SimpleFilter filter, SpecimenVisitReportParameters parameters) + { + super(titlePrefix, visits, filter, parameters); + _showCohorts = StudyService.get().showCohorts(_container, getUser()); + } + + @Override + public Collection createRows() + { + CohortFilter.Type cohortType = getCohortFilter() != null ? getCohortFilter().getType() : CohortFilter.Type.DATA_COLLECTION; + Collection countSummary = + SpecimenReportManager.get().getParticipantSummaryByVisitType(_container, getUser(), _filter, getBaseCustomView(), cohortType); + Map rows = new TreeMap<>(); + for (SummaryByVisitParticipant count : countSummary) + { + String cohort = _showCohorts ? count.getCohort() : "[cohort blinded]"; + if (cohort == null || cohort.length() == 0) + cohort = "[No cohort assigned]"; + String key = cohort + "/" + count.getParticipantId(); + Row row = rows.get(key); + if (row == null) + { + String ptid = count.getParticipantId(); + SpecimenReportTitle ptidTitle = new SpecimenReportTitle(ptid, DemoMode.id(ptid, getContainer(), getUser())); + SpecimenReportTitle[] titleHierarchy; + if (_showCohorts) + titleHierarchy = new SpecimenReportTitle[] {new SpecimenReportTitle(cohort), ptidTitle}; + else + titleHierarchy = new SpecimenReportTitle[] {ptidTitle}; + row = new Row(titleHierarchy); + rows.put(key, row); + } + setVisitAsNonEmpty(count.getVisit()); + row.add(count); + } + return rows.values(); + } + + @Override + public int getLabelDepth() + { + return _showCohorts ? 2 : 1; + } + + @Override + protected String[] getCellExcelText(Visit visit, SummaryByVisitParticipant summary) + { + if (summary == null || summary.getVialCount() == null) + return new String[] {}; + + StringBuilder summaryString = new StringBuilder(); + if (isViewVialCount()) + summaryString.append(summary.getVialCount()); + if (isViewVolume()) + { + if (summaryString.length() > 0) + summaryString.append("/"); + summaryString.append(summary.getTotalVolume()); + } + return new String[] { summaryString.toString() }; + } + + @Override + protected String getCellHtml(Visit visit, SummaryByVisitParticipant summary) + { + if (summary == null || summary.getVialCount() == null) + return " "; + ActionURL link = PageFlowUtil.urlProvider(SpecimenUrls.class).getSpecimensURL(_container, true); + link = updateURLFilterParameter(link, "SpecimenDetail.Visit/SequenceNumMin", visit.getSequenceNumMinDouble()); + link = updateURLFilterParameter(link, "SpecimenDetail." + StudyService.get().getSubjectColumnName(getContainer()), summary.getParticipantId()); + String linkHtml = link.getLocalURIString(); + if (_filter != null) + { + String filterQueryString = getFilterQueryString(visit, summary); + if (filterQueryString != null && filterQueryString.length() > 0) + linkHtml += "&" + filterQueryString; + } + return buildCellHtml(visit, summary, linkHtml); + } +} diff --git a/specimen/src/org/labkey/specimen/report/request/BaseRequestReportFactory.java b/specimen/src/org/labkey/specimen/report/request/BaseRequestReportFactory.java new file mode 100644 index 00000000..3bda3972 --- /dev/null +++ b/specimen/src/org/labkey/specimen/report/request/BaseRequestReportFactory.java @@ -0,0 +1,71 @@ +/* + * 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.specimen.report.request; + +import org.labkey.api.security.User; +import org.labkey.api.util.HtmlString; +import org.labkey.api.util.Pair; +import org.labkey.api.util.element.Option; +import org.labkey.api.util.element.Select; +import org.labkey.specimen.report.specimentype.TypeReportFactory; + +import java.util.List; + +import static org.labkey.api.util.HtmlString.unsafe; + +/** + * User: brittp + * Date: Jun 25, 2009 + * Time: 10:57:36 AM + */ +public abstract class BaseRequestReportFactory extends TypeReportFactory +{ + private boolean _completedRequestsOnly = false; + + public boolean isCompletedRequestsOnly() + { + return _completedRequestsOnly; + } + + public void setCompletedRequestsOnly(boolean completedRequestsOnly) + { + _completedRequestsOnly = completedRequestsOnly; + } + + @Override + public List> getAdditionalFormInputHtml(User user) + { + List> superInputs = super.getAdditionalFormInputHtml(user); + + Select.SelectBuilder sb = new Select.SelectBuilder(); + + sb.addOption(new Option.OptionBuilder() + .value(Boolean.toString(false)) + .label("Include in-process requests") + .build() + ); + + sb.addOption(new Option.OptionBuilder() + .value(Boolean.toString(true)) + .label("Completed requests only") + .selected(_completedRequestsOnly) + .build() + ); + + superInputs.add(new Pair<>("Request status", unsafe(sb.toString()))); + return superInputs; + } +} diff --git a/specimen/src/org/labkey/specimen/report/request/RequestEnrollmentLocationReport.java b/specimen/src/org/labkey/specimen/report/request/RequestEnrollmentLocationReport.java new file mode 100644 index 00000000..27836058 --- /dev/null +++ b/specimen/src/org/labkey/specimen/report/request/RequestEnrollmentLocationReport.java @@ -0,0 +1,51 @@ +/* + * 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.specimen.report.request; + +import org.labkey.api.data.SimpleFilter; +import org.labkey.specimen.report.SummaryByVisitType; +import org.labkey.api.study.Visit; +import org.labkey.api.specimen.query.SpecimenQueryView; +import org.labkey.specimen.report.SpecimenTypeVisitReport; +import org.labkey.specimen.report.SpecimenVisitReportParameters; + +import java.util.List; + +/** + * User: brittp + * Created: Jan 14, 2008 1:37:24 PM + */ +public class RequestEnrollmentLocationReport extends SpecimenTypeVisitReport +{ + private final int _locationId; + private final boolean _completedRequestsOnly; + + public RequestEnrollmentLocationReport(String titlePrefix, SimpleFilter filter, SpecimenVisitReportParameters parameters, + List visits, int locationId, boolean completedRequestsOnly) + { + super(titlePrefix, visits, filter, parameters); + _locationId = locationId; + _completedRequestsOnly = completedRequestsOnly; + } + + @Override + protected String getFilterQueryString(Visit visit, SummaryByVisitType summary) + { + return super.getFilterQueryString(visit, summary) + "&" + + (_completedRequestsOnly ? SpecimenQueryView.PARAMS.showCompleteRequestedByEnrollmentSite : + SpecimenQueryView.PARAMS.showRequestedByEnrollmentSite) + "=" + _locationId; + } +} \ No newline at end of file diff --git a/specimen/src/org/labkey/specimen/report/request/RequestEnrollmentSiteReportFactory.java b/specimen/src/org/labkey/specimen/report/request/RequestEnrollmentSiteReportFactory.java new file mode 100644 index 00000000..299e8356 --- /dev/null +++ b/specimen/src/org/labkey/specimen/report/request/RequestEnrollmentSiteReportFactory.java @@ -0,0 +1,132 @@ +/* + * 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.specimen.report.request; + +import org.labkey.api.data.SQLFragment; +import org.labkey.api.data.SimpleFilter; +import org.labkey.api.query.FieldKey; +import org.labkey.api.security.User; +import org.labkey.api.specimen.SpecimenManager; +import org.labkey.api.specimen.location.LocationImpl; +import org.labkey.api.specimen.location.LocationManager; +import org.labkey.api.specimen.query.SpecimenQueryView; +import org.labkey.specimen.report.SpecimenVisitReport; +import org.labkey.specimen.report.SpecimenVisitReportAction; +import org.labkey.specimen.actions.SpecimenReportActions; +import org.labkey.api.study.Location; +import org.labkey.api.study.Visit; +import org.labkey.api.util.HtmlString; +import org.labkey.api.util.Pair; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Set; + +/** + * User: brittp + * Created: Jan 24, 2008 1:38:40 PM + */ +public class RequestEnrollmentSiteReportFactory extends BaseRequestReportFactory +{ + private Integer _enrollmentSiteId; + + public Integer getEnrollmentSiteId() + { + return _enrollmentSiteId; + } + + public void setEnrollmentSiteId(Integer enrollmentSiteId) + { + _enrollmentSiteId = enrollmentSiteId; + } + + @Override + public boolean allowsAvailabilityFilter() + { + return false; + } + + @Override + public String getLabel() + { + Location location = _enrollmentSiteId != null ? LocationManager.get().getLocation(getContainer(), _enrollmentSiteId) : null; + return "Requested by Enrollment Location" + (location != null ? ": " + location.getLabel() : ""); + } + + @Override + public String getReportType() + { + return "RequestedByEnrollmentLocation"; + } + + @Override + protected List createReports() + { + final Set locations; + + if (getEnrollmentSiteId() != null) + { + locations = Collections.singleton(LocationManager.get().getLocation(getContainer(), getEnrollmentSiteId())); + } + else + { + locations = SpecimenManager.get().getEnrollmentSitesWithRequests(getContainer(), getUser()); + // add null to the set so we can search for ptid without an enrollment site: + locations.add(null); + } + + List reports = new ArrayList<>(); + List visits = SpecimenManager.get().getVisitsWithSpecimens(getContainer(), getUser(), getCohort()); + + SQLFragment baseSql = SpecimenQueryView.getBaseRequestedEnrollmentSql(getContainer(), getUser(), isCompletedRequestsOnly()); + + for (LocationImpl location : locations) + { + SimpleFilter filter = new SimpleFilter(); + SQLFragment sql = new SQLFragment(baseSql); + if (location == null) + sql.append("IS NULL)"); + else + { + sql.append("= " + location.getRowId() + ")"); + } + + filter.addWhereClause(sql, FieldKey.fromParts("GlobalUniqueId")); + addBaseFilters(filter); + reports.add(new RequestEnrollmentLocationReport(location == null ? "[Unassigned enrollment location]" : location.getLabel(), + filter, this, visits, location != null ? location.getRowId() : -1, isCompletedRequestsOnly())); + } + return reports; + } + + @Override + public Class getAction() + { + return SpecimenReportActions.RequestEnrollmentSiteReportAction.class; + } + + @Override + public List> getAdditionalFormInputHtml(User user) + { + List> inputs = new ArrayList<>(super.getAdditionalFormInputHtml(user)); + Set locations = SpecimenManager.get().getEnrollmentSitesWithRequests(getContainer(), getUser()); + // add null to the set so we can search for ptid without an enrollment site: + locations.add(null); + inputs.add(getEnrollmentSitePicker("enrollmentSiteId", locations, _enrollmentSiteId)); + return inputs; + } +} diff --git a/specimen/src/org/labkey/specimen/report/request/RequestLocationReport.java b/specimen/src/org/labkey/specimen/report/request/RequestLocationReport.java new file mode 100644 index 00000000..48918b48 --- /dev/null +++ b/specimen/src/org/labkey/specimen/report/request/RequestLocationReport.java @@ -0,0 +1,49 @@ +/* + * 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.specimen.report.request; + +import org.labkey.api.data.SimpleFilter; +import org.labkey.specimen.report.SummaryByVisitType; +import org.labkey.api.study.Visit; +import org.labkey.api.specimen.query.SpecimenQueryView; +import org.labkey.specimen.report.SpecimenTypeVisitReport; +import org.labkey.specimen.report.SpecimenVisitReportParameters; + +import java.util.List; + +/** + * User: brittp + * Created: Jan 14, 2008 1:37:24 PM + */ +public class RequestLocationReport extends SpecimenTypeVisitReport +{ + private final int _locationId; + private final boolean _completeRequestsOnly; + + public RequestLocationReport(String titlePrefix, SimpleFilter filter, SpecimenVisitReportParameters parameters, List visits, int locationId, boolean completeRequestsOnly) + { + super(titlePrefix, visits, filter, parameters); + _locationId = locationId; + _completeRequestsOnly = completeRequestsOnly; + } + + @Override + protected String getFilterQueryString(Visit visit, SummaryByVisitType summary) + { + return super.getFilterQueryString(visit, summary) + "&" + + (_completeRequestsOnly ? SpecimenQueryView.PARAMS.showCompleteRequestedBySite : SpecimenQueryView.PARAMS.showRequestedBySite) + "=" + _locationId; + } +} \ No newline at end of file diff --git a/specimen/src/org/labkey/specimen/report/request/RequestLocationReportFactory.java b/specimen/src/org/labkey/specimen/report/request/RequestLocationReportFactory.java new file mode 100644 index 00000000..e28b022f --- /dev/null +++ b/specimen/src/org/labkey/specimen/report/request/RequestLocationReportFactory.java @@ -0,0 +1,144 @@ +/* + * 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.specimen.report.request; + +import org.labkey.api.data.SimpleFilter; +import org.labkey.api.security.User; +import org.labkey.api.specimen.SpecimenManager; +import org.labkey.api.specimen.location.LocationImpl; +import org.labkey.api.specimen.location.LocationManager; +import org.labkey.specimen.report.SpecimenVisitReport; +import org.labkey.specimen.report.SpecimenVisitReportAction; +import org.labkey.specimen.actions.SpecimenReportActions; +import org.labkey.api.study.Location; +import org.labkey.api.study.Visit; +import org.labkey.api.util.HtmlString; +import org.labkey.api.util.Pair; +import org.labkey.api.util.element.Option; +import org.labkey.api.util.element.Select; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import static org.labkey.api.util.HtmlString.unsafe; + +/** + * User: brittp + * Created: Jan 24, 2008 1:38:40 PM + */ +public class RequestLocationReportFactory extends BaseRequestReportFactory +{ + private Integer _locationId; + + public Integer getLocationId() + { + return _locationId; + } + + public void setLocationId(Integer locationId) + { + _locationId = locationId; + } + + @Override + public boolean allowsAvailabilityFilter() + { + return false; + } + + @Override + public String getLabel() + { + Location location = _locationId != null ? LocationManager.get().getLocation(getContainer(), _locationId) : null; + return "Requested by Requesting Location" + (location != null ? ": " + location.getLabel() : ""); + } + + @Override + public String getReportType() + { + return "RequestedByRequestingLocation"; + } + + @Override + protected List createReports() + { + LocationImpl[] locations; + if (getLocationId() != null) + locations = new LocationImpl[] { LocationManager.get().getLocation(getContainer(), getLocationId()) }; + else + locations = SpecimenManager.get().getSitesWithRequests(getContainer()); + if (locations == null) + return Collections.emptyList(); + List reports = new ArrayList<>(); + List visits = SpecimenManager.get().getVisitsWithSpecimens(getContainer(), getUser(), getCohort()); + for (LocationImpl location : locations) + { + SimpleFilter filter = new SimpleFilter(); + Object[] params; + if (isCompletedRequestsOnly()) + params = new Object[] { Boolean.TRUE, Boolean.TRUE, location.getRowId(), getContainer().getId()}; + else + params = new Object[] { Boolean.TRUE, location.getRowId(), getContainer().getId()}; + filter.addWhereClause("globaluniqueid IN\n" + + "(\n" + + " SELECT specimenglobaluniqueid FROM study.samplerequestspecimen WHERE samplerequestid IN\n" + + " (\n" + + " SELECT r.rowid FROM study.SampleRequest r JOIN study.SampleRequestStatus rs ON\n" + + " r.StatusId = rs.RowId\n" + + " AND rs.SpecimensLocked = ?\n" + + (isCompletedRequestsOnly() ? " AND rs.FinalState = ?\n" : "") + + " WHERE destinationsiteid = ? AND r.container = ?\n" + + " )\n" + + ")", params); + addBaseFilters(filter); + reports.add(new RequestLocationReport(location.getLabel(), filter, this, visits, location.getRowId(), isCompletedRequestsOnly())); + } + return reports; + } + + @Override + public Class getAction() + { + return SpecimenReportActions.RequestSiteReportAction.class; + } + + @Override + public List> getAdditionalFormInputHtml(User user) + { + + Select.SelectBuilder builder = new Select.SelectBuilder(); + + builder.name("locationId") + .addOption(new Option.OptionBuilder() + .value("") + .label("All Requesting Locations") + .build()); + + for (LocationImpl location : SpecimenManager.get().getSitesWithRequests(getContainer())) + { + builder.addOption(new Option.OptionBuilder() + .value(Integer.toString(location.getRowId())) + .label(location.getLabel()) + .selected(_locationId != null && location.getRowId() == _locationId) + .build()); + } + + List> inputs = new ArrayList<>(super.getAdditionalFormInputHtml(user)); + inputs.add(new Pair<>("Requesting location(s)", unsafe(builder.toString()))); + return inputs; + } +} \ No newline at end of file diff --git a/specimen/src/org/labkey/specimen/report/request/RequestParticipantReport.java b/specimen/src/org/labkey/specimen/report/request/RequestParticipantReport.java new file mode 100644 index 00000000..90ae82d4 --- /dev/null +++ b/specimen/src/org/labkey/specimen/report/request/RequestParticipantReport.java @@ -0,0 +1,120 @@ +/* + * 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.specimen.report.request; + +import org.labkey.api.data.SimpleFilter; +import org.labkey.api.specimen.query.SpecimenQueryView; +import org.labkey.api.study.SpecimenUrls; +import org.labkey.api.study.Visit; +import org.labkey.api.util.PageFlowUtil; +import org.labkey.api.view.ActionURL; +import org.labkey.specimen.report.RequestSummaryByVisitType; +import org.labkey.specimen.report.SpecimenReportManager; +import org.labkey.specimen.report.SpecimenTypeLevel; +import org.labkey.specimen.report.SpecimenVisitReport; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +/** + * User: brittp + * Created: Feb 5, 2008 2:17:53 PM + */ +public class RequestParticipantReport extends SpecimenVisitReport +{ + private final boolean _completeRequestsOnly; + + public RequestParticipantReport(String titlePrefix, List visits, SimpleFilter filter, RequestParticipantReportFactory parameters) + { + super(titlePrefix, visits, filter, parameters); + _completeRequestsOnly = parameters.isCompletedRequestsOnly(); + } + + @Override + public Collection createRows() + { + SpecimenTypeLevel level = getTypeLevelEnum(); + RequestSummaryByVisitType[] countSummary = + SpecimenReportManager.get().getRequestSummaryBySite(_container, getUser(), _filter, + isViewPtidList(), level, getBaseCustomView(), _completeRequestsOnly); + Map rows = new TreeMap<>(); + for (RequestSummaryByVisitType count : countSummary) + { + String key = count.getSiteLabel() + "/" + count.getPrimaryType() + "/" + + (count.getDerivative() != null ? count.getDerivative() : "All") + "/" + + (count.getAdditive() != null ? count.getAdditive() : "All"); + Row row = rows.get(key); + if (row == null) + { + String[] typeHierarchy = level.getTitleHierarchy(count); + String[] titleHierarchy = new String[typeHierarchy.length + 1]; + titleHierarchy[0] = count.getSiteLabel(); + System.arraycopy(typeHierarchy, 0, titleHierarchy, 1, typeHierarchy.length); + row = new Row(titleHierarchy); + rows.put(key, row); + } + setVisitAsNonEmpty(count.getVisit()); + row.add(count); + } + return rows.values(); + } + + @Override + protected String[] getCellExcelText(Visit visit, RequestSummaryByVisitType summary) + { + if (summary == null || summary.getVialCount() == null) + return new String[] {}; + StringBuilder summaryString = new StringBuilder(); + if (isViewVialCount()) + summaryString.append(summary.getVialCount()); + if (isViewVolume()) + { + if (summaryString.length() > 0) + summaryString.append("/"); + summaryString.append(summary.getTotalVolume()); + } + return new String[] { summaryString.toString() }; + } + + @Override + protected String getCellHtml(Visit visit, RequestSummaryByVisitType summary) + { + if (summary == null || summary.getVialCount() == null) + return " "; + ActionURL url = PageFlowUtil.urlProvider(SpecimenUrls.class).getSpecimensURL(_container, true); + url = updateURLFilterParameter(url, "SpecimenDetail.Visit/SequenceNumMin", visit.getSequenceNumMinDouble()); + url = updateURLFilterParameter(url, "SpecimenDetail.PrimaryType/Description", summary.getPrimaryType()); + SpecimenTypeLevel level = getTypeLevelEnum(); + if (level == SpecimenTypeLevel.Derivative || level == SpecimenTypeLevel.Additive) + url = updateURLFilterParameter(url, "SpecimenDetail.DerivativeType/Description", summary.getDerivative()); + if (level == SpecimenTypeLevel.Additive) + url = updateURLFilterParameter(url, "SpecimenDetail.AdditiveType/Description", summary.getAdditive()); + String linkHtml = url.getLocalURIString(); + if (_filter != null) + linkHtml += "&" + getFilterQueryString(visit, summary); + return buildCellHtml(visit, summary, linkHtml); + } + + @Override + protected String getFilterQueryString(Visit visit, RequestSummaryByVisitType summary) + { + return super.getFilterQueryString(visit, summary) + "&" + + (_completeRequestsOnly ? SpecimenQueryView.PARAMS.showCompleteRequestedBySite : SpecimenQueryView.PARAMS.showRequestedBySite) + + "=" + summary.getDestinationSiteId(); + } +} diff --git a/specimen/src/org/labkey/specimen/report/request/RequestParticipantReportFactory.java b/specimen/src/org/labkey/specimen/report/request/RequestParticipantReportFactory.java new file mode 100644 index 00000000..d5a016d7 --- /dev/null +++ b/specimen/src/org/labkey/specimen/report/request/RequestParticipantReportFactory.java @@ -0,0 +1,167 @@ +/* + * 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.specimen.report.request; + +import org.labkey.api.data.SimpleFilter; +import org.labkey.api.query.FieldKey; +import org.labkey.api.security.User; +import org.labkey.api.specimen.SpecimenManager; +import org.labkey.specimen.report.SpecimenVisitReport; +import org.labkey.specimen.report.SpecimenVisitReportAction; +import org.labkey.specimen.actions.SpecimenReportActions; +import org.labkey.api.study.Cohort; +import org.labkey.api.study.Study; +import org.labkey.api.study.StudyService; +import org.labkey.api.study.Visit; +import org.labkey.api.study.model.CohortService; +import org.labkey.api.study.model.ParticipantGroupService; +import org.labkey.api.study.model.VisitService; +import org.labkey.api.util.DemoMode; +import org.labkey.api.util.HtmlString; +import org.labkey.api.util.Pair; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * User: brittp + * Created: Feb 5, 2008 2:03:52 PM + */ +public class RequestParticipantReportFactory extends BaseRequestReportFactory +{ + private String _participantId; + + public String getParticipantId() + { + return _participantId; + } + + public void setParticipantId(String participantId) + { + _participantId = participantId; + } + + @Override + public boolean allowsCohortFilter() + { + return false; + } + + @Override + public boolean allowsAvailabilityFilter() + { + return false; + } + + @Override + public boolean allowsParticipantAggregates() + { + return false; + } + + @Override + public boolean allowsParticipantGroupFilter() + { + return false; + } + + @Override + public List> getAdditionalFormInputHtml(User user) + { + List> inputs = new ArrayList<>(super.getAdditionalFormInputHtml(user)); + inputs.add(getParticipantPicker("participantId", _participantId)); + return inputs; + } + + @Override + protected List createReports() + { + Collection participantIds; + if (!isAllSubjectsOption(_participantId) && _participantId != null && _participantId.trim().length() > 0) + { + Study study = StudyService.get().getStudy(getContainer()); + if (!StudyService.get().participantExists(study, _participantId)) + return Collections.emptyList(); + participantIds = List.of(_participantId); + } + else + { + Study study = StudyService.get().getStudy(getContainer()); + if (getParticipantGroupFilter() >= 0) + participantIds = ParticipantGroupService.get().getParticipantIdsForGroup(study, getUser(), getParticipantGroupFilter()); + else + participantIds = StudyService.get().getParticipantIds(study, getUser()); + if (participantIds == null) + return Collections.emptyList(); + } + + List reports = new ArrayList<>(); + Map> visitListCache = new HashMap<>(); // cohort rowId -> visit + boolean showCohorts = StudyService.get().showCohorts(getContainer(), getUser()); + List allVisits = null; + Study study = StudyService.get().getStudy(getContainer()); + for (String participantId : participantIds) + { + SimpleFilter filter = new SimpleFilter(FieldKey.fromParts(StudyService.get().getSubjectColumnName(getContainer())), participantId); + addBaseFilters(filter); + List visits = null; + if (showCohorts) + { + Cohort cohort = CohortService.get().getCurrentCohortForParticipant(getContainer(), getUser(), participantId); + if (cohort != null) + { + visits = visitListCache.get(cohort.getRowId()); + if (visits == null) + { + visits = SpecimenManager.get().getVisitsWithSpecimens(getContainer(), getUser(), cohort); + visitListCache.put(cohort.getRowId(), visits); + } + } + } + + if (visits == null) + { + if (allVisits == null) + allVisits = VisitService.get().getVisits(study, Visit.Order.DISPLAY); + visits = allVisits; + } + reports.add(new RequestParticipantReport(DemoMode.id(participantId, getContainer(), getUser()), visits, filter, this)); + } + return reports; + } + + @Override + public String getLabel() + { + return "Requested by " + StudyService.get().getSubjectNounSingular(getContainer()); + } + + @Override + public String getReportType() + { + return "RequestedByParticipant"; + } + + @Override + public Class getAction() + { + return SpecimenReportActions.RequestParticipantReportAction.class; + } +} diff --git a/specimen/src/org/labkey/specimen/report/request/RequestReport.java b/specimen/src/org/labkey/specimen/report/request/RequestReport.java new file mode 100644 index 00000000..03fefa7e --- /dev/null +++ b/specimen/src/org/labkey/specimen/report/request/RequestReport.java @@ -0,0 +1,47 @@ +/* + * 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.specimen.report.request; + +import org.labkey.api.data.SimpleFilter; +import org.labkey.specimen.report.SummaryByVisitType; +import org.labkey.api.study.Visit; +import org.labkey.api.specimen.query.SpecimenQueryView; +import org.labkey.specimen.report.SpecimenTypeVisitReport; +import org.labkey.specimen.report.SpecimenVisitReportParameters; + +import java.util.List; + +/** + * User: brittp + * Created: Jan 14, 2008 1:37:24 PM + */ +public class RequestReport extends SpecimenTypeVisitReport +{ + private final boolean _completedRequestsOnly; + + public RequestReport(String titlePrefix, SimpleFilter filter, SpecimenVisitReportParameters parameters, List visits, boolean completedRequestsOnly) + { + super(titlePrefix, visits, filter, parameters); + _completedRequestsOnly = completedRequestsOnly; + } + + @Override + protected String getFilterQueryString(Visit visit, SummaryByVisitType summary) + { + return super.getFilterQueryString(visit, summary) + "&" + + (_completedRequestsOnly ? SpecimenQueryView.PARAMS.showCompleteRequestedOnly : SpecimenQueryView.PARAMS.showRequestedOnly) + "=true"; + } +} \ No newline at end of file diff --git a/specimen/src/org/labkey/specimen/report/request/RequestReportFactory.java b/specimen/src/org/labkey/specimen/report/request/RequestReportFactory.java new file mode 100644 index 00000000..95adc06f --- /dev/null +++ b/specimen/src/org/labkey/specimen/report/request/RequestReportFactory.java @@ -0,0 +1,85 @@ +/* + * 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.specimen.report.request; + +import org.labkey.api.data.SimpleFilter; +import org.labkey.api.specimen.SpecimenManager; +import org.labkey.specimen.report.SpecimenVisitReport; +import org.labkey.specimen.report.SpecimenVisitReportAction; +import org.labkey.specimen.actions.SpecimenReportActions; +import org.labkey.api.study.Location; +import org.labkey.api.study.Visit; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * User: brittp + * Created: Jan 24, 2008 1:38:40 PM + */ +public class RequestReportFactory extends BaseRequestReportFactory +{ + @Override + public String getLabel() + { + return "Requested Summary"; + } + + @Override + public String getReportType() + { + return "RequestedSummary"; + } + + @Override + public boolean allowsAvailabilityFilter() + { + return false; + } + + @Override + protected List createReports() + { + Location[] locations = SpecimenManager.get().getSitesWithRequests(getContainer()); + if (locations == null) + return Collections.emptyList(); + List reports = new ArrayList<>(); + List visits = SpecimenManager.get().getVisitsWithSpecimens(getContainer(), getUser(), getCohort()); + SimpleFilter filter = new SimpleFilter(); + if (isCompletedRequestsOnly()) + { + filter.addWhereClause("globaluniqueid IN\n" + + "(" + COMPLETED_REQUESTS_FILTER_SQL + ")", + new Object[] { Boolean.TRUE, Boolean.TRUE, getContainer().getId()}); + } + else + { + filter.addWhereClause("globaluniqueid IN\n" + + "(SELECT specimenglobaluniqueid FROM study.samplerequestspecimen WHERE container = ?) and LockedInRequest = ?", + new Object[] { getContainer().getId(), Boolean.TRUE }); + } + addBaseFilters(filter); + reports.add(new RequestReport("All Requested Specimens", filter, this, visits, isCompletedRequestsOnly())); + return reports; + } + + @Override + public Class getAction() + { + return SpecimenReportActions.RequestReportAction.class; + } +} diff --git a/specimen/src/org/labkey/specimen/report/specimentype/TypeCohortReport.java b/specimen/src/org/labkey/specimen/report/specimentype/TypeCohortReport.java new file mode 100644 index 00000000..5ea79986 --- /dev/null +++ b/specimen/src/org/labkey/specimen/report/specimentype/TypeCohortReport.java @@ -0,0 +1,51 @@ +/* + * 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.specimen.report.specimentype; + +import org.labkey.api.data.SimpleFilter; +import org.labkey.api.study.CohortFilter; +import org.labkey.api.study.Study; +import org.labkey.api.study.Visit; +import org.labkey.api.view.ActionURL; +import org.labkey.specimen.report.SpecimenTypeVisitReport; +import org.labkey.specimen.report.SpecimenVisitReportParameters; + +import java.util.List; + +/** + * User: brittp + * Created: Jan 14, 2008 1:37:24 PM + */ +public class TypeCohortReport extends SpecimenTypeVisitReport +{ + private final CohortFilter _cohortFilter; + + public TypeCohortReport(String titlePrefix, List visits, SimpleFilter filter, SpecimenVisitReportParameters parameters, CohortFilter cohortFilter) + { + super(titlePrefix, visits, filter, parameters); + _cohortFilter = cohortFilter; + } + + // override addCohortURLFilter to use the filter we're passed, rather than the cohort filter on the base + // parameters object (the base parameter filter just contains the type, since this report can render multiple + // reports, one for each cohort): + @Override + protected void addCohortURLFilter(Study study, ActionURL url) + { + if (_cohortFilter != null) + _cohortFilter.addURLParameters(study, url, null); + } +} diff --git a/specimen/src/org/labkey/specimen/report/specimentype/TypeCohortReportFactory.java b/specimen/src/org/labkey/specimen/report/specimentype/TypeCohortReportFactory.java new file mode 100644 index 00000000..49c50799 --- /dev/null +++ b/specimen/src/org/labkey/specimen/report/specimentype/TypeCohortReportFactory.java @@ -0,0 +1,126 @@ +/* + * 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.specimen.report.specimentype; + +import org.labkey.api.data.SimpleFilter; +import org.labkey.api.security.User; +import org.labkey.api.specimen.SpecimenManager; +import org.labkey.specimen.report.SpecimenVisitReport; +import org.labkey.specimen.report.SpecimenVisitReportAction; +import org.labkey.specimen.actions.SpecimenReportActions; +import org.labkey.api.study.Cohort; +import org.labkey.api.study.CohortFilter; +import org.labkey.api.study.Params; +import org.labkey.api.study.Study; +import org.labkey.api.study.StudyService; +import org.labkey.api.study.Visit; +import org.labkey.api.study.model.CohortService; +import org.labkey.api.util.HtmlString; +import org.labkey.api.util.Pair; +import org.labkey.api.util.element.Input; +import org.labkey.api.util.element.Option; +import org.labkey.api.util.element.Select; + +import java.util.ArrayList; +import java.util.List; + +import static org.labkey.api.util.HtmlString.unsafe; + +/** + * User: brittp + * Created: Jan 24, 2008 3:19:29 PM + */ +public class TypeCohortReportFactory extends TypeReportFactory +{ + @Override + public boolean allowsCohortFilter() + { + return false; + } + + @Override + public Class getAction() + { + return SpecimenReportActions.TypeCohortReportAction.class; + } + + @Override + public List> getAdditionalFormInputHtml(User user) + { + List> inputs = super.getAdditionalFormInputHtml(user); + Study study = StudyService.get().getStudy(getContainer()); + if (study.isAdvancedCohorts()) + { + CohortFilter.Type currentType = getCohortFilter() != null ? getCohortFilter().getType() : CohortFilter.Type.DATA_COLLECTION; + Input.InputBuilder input = new Input.InputBuilder() + .type("hidden") + .value("0") + .name(Params.cohortId.name()); + + Select.SelectBuilder select = new Select.SelectBuilder() + .name(Params.cohortFilterType.name()); + + for (CohortFilter.Type type : CohortFilter.Type.values()) + { + select.addOption(new Option.OptionBuilder() + .value(type.name()) + .label(type.getTitle()) + .selected(type == currentType) + .build() + ); + } + + String combinedElements = input.toString().concat(select.toString()); + inputs.add(new Pair<>("Cohort Filter Type", unsafe(combinedElements))); + } + return inputs; + } + + @Override + protected List createReports() + { + CohortFilter.Type type = getCohortFilter() != null ? getCohortFilter().getType() : CohortFilter.Type.DATA_COLLECTION; + List reportCohorts = new ArrayList<>(CohortService.get().getCohortFilters(type, getContainer(), getUser())); + reportCohorts.add(CohortService.get().getUnassignedCohortFilter()); + + List reports = new ArrayList<>(); + for (CohortFilter cohortFilter : reportCohorts) + { + Cohort cohort = cohortFilter.getCohort(getContainer(), getUser()); + String title = cohort != null ? cohort.getLabel() : "[No cohort assigned]"; + SimpleFilter filter = new SimpleFilter(); + addBaseFilters(filter); + addCohortFilter(filter, cohortFilter); + List visits = SpecimenManager.get().getVisitsWithSpecimens(getContainer(), getUser(), cohort); + reports.add(new TypeCohortReport(title, visits, filter, this, cohortFilter)); + } + return reports; + } + + @Override + public String getLabel() + { + CohortFilter filter = getCohortFilter(); + Cohort cohort = filter != null ? filter.getCohort(getContainer(), getUser()) : null; + return cohort != null ? cohort.getLabel() : "Type by Cohort"; + } + + @Override + public String getReportType() + { + return "TypeByCohort"; + } +} diff --git a/specimen/src/org/labkey/specimen/report/specimentype/TypeParticipantReportFactory.java b/specimen/src/org/labkey/specimen/report/specimentype/TypeParticipantReportFactory.java new file mode 100644 index 00000000..19c55652 --- /dev/null +++ b/specimen/src/org/labkey/specimen/report/specimentype/TypeParticipantReportFactory.java @@ -0,0 +1,156 @@ +/* + * 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.specimen.report.specimentype; + +import org.labkey.api.data.SimpleFilter; +import org.labkey.api.query.FieldKey; +import org.labkey.api.security.User; +import org.labkey.api.specimen.SpecimenManager; +import org.labkey.specimen.report.SpecimenTypeVisitReport; +import org.labkey.specimen.report.SpecimenVisitReport; +import org.labkey.specimen.report.SpecimenVisitReportAction; +import org.labkey.specimen.actions.SpecimenReportActions; +import org.labkey.api.study.Cohort; +import org.labkey.api.study.Study; +import org.labkey.api.study.StudyService; +import org.labkey.api.study.Visit; +import org.labkey.api.study.model.CohortService; +import org.labkey.api.study.model.ParticipantGroupService; +import org.labkey.api.study.model.VisitService; +import org.labkey.api.util.DemoMode; +import org.labkey.api.util.HtmlString; +import org.labkey.api.util.Pair; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * User: brittp + * Created: Jan 24, 2008 1:38:15 PM + */ +public class TypeParticipantReportFactory extends TypeReportFactory +{ + private String _participantId; + + public String getParticipantId() + { + return _participantId; + } + + public void setParticipantId(String participantId) + { + _participantId = participantId; + } + + @Override + public String getLabel() + { + String subjectNoun = StudyService.get().getSubjectNounSingular(getContainer()); + return _participantId == null || isAllSubjectsOption(_participantId) ? "Type by " + subjectNoun : subjectNoun + " " + DemoMode.id(_participantId, getContainer(), getUser()); + } + + @Override + public String getReportType() + { + return "TypeByParticipant"; + } + + @Override + public boolean allowsCohortFilter() + { + return false; + } + + @Override + public boolean allowsParticipantAggregates() + { + return false; + } + + @Override + protected List createReports() + { + Collection participantIds; + if (!isAllSubjectsOption(_participantId) && _participantId != null && _participantId.trim().length() > 0) + { + Study study = StudyService.get().getStudy(getContainer()); + if (!StudyService.get().participantExists(study, _participantId)) + return Collections.emptyList(); + participantIds = List.of(_participantId); + } + else + { + Study study = StudyService.get().getStudy(getContainer()); + if (getParticipantGroupFilter() >= 0) + participantIds = ParticipantGroupService.get().getParticipantIdsForGroup(study, getUser(), getParticipantGroupFilter()); + else + participantIds = StudyService.get().getParticipantIds(study, getUser()); + if (participantIds == null) + return Collections.emptyList(); + } + List reports = new ArrayList<>(); + Map> visitListCache = new HashMap<>(); // cohort rowId -> visits + boolean showCohorts = StudyService.get().showCohorts(getContainer(), getUser()); + List allVisits = null; + Study study = StudyService.get().getStudy(getContainer()); + for (String participantId : participantIds) + { + SimpleFilter filter = new SimpleFilter(FieldKey.fromParts(StudyService.get().getSubjectColumnName(getContainer())), participantId); + addBaseFilters(filter); + List visits = null; + if (showCohorts) + { + Cohort cohort = CohortService.get().getCurrentCohortForParticipant(getContainer(), getUser(), participantId); + if (cohort != null) + { + visits = visitListCache.get(cohort.getRowId()); + if (visits == null) + { + visits = SpecimenManager.get().getVisitsWithSpecimens(getContainer(), getUser(), cohort); + visitListCache.put(cohort.getRowId(), visits); + } + } + } + + if (visits == null) + { + if (allVisits == null) + allVisits = VisitService.get().getVisits(study, Visit.Order.DISPLAY); + visits = allVisits; + } + reports.add(new SpecimenTypeVisitReport(DemoMode.id(participantId, getContainer(), getUser()), visits, filter, this)); + } + return reports; + } + + @Override + public Class getAction() + { + return SpecimenReportActions.TypeParticipantReportAction.class; + } + + @Override + public List> getAdditionalFormInputHtml(User user) + { + List> inputs = new ArrayList<>(super.getAdditionalFormInputHtml(user)); + inputs.add(getParticipantPicker("participantId", _participantId)); + return inputs; + } +} diff --git a/specimen/src/org/labkey/specimen/report/specimentype/TypeReportFactory.java b/specimen/src/org/labkey/specimen/report/specimentype/TypeReportFactory.java new file mode 100644 index 00000000..556a9bc0 --- /dev/null +++ b/specimen/src/org/labkey/specimen/report/specimentype/TypeReportFactory.java @@ -0,0 +1,62 @@ +/* + * 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.specimen.report.specimentype; + +import org.labkey.api.security.User; +import org.labkey.specimen.report.SpecimenTypeLevel; +import org.labkey.api.study.SpecimenService; +import org.labkey.api.util.HtmlString; +import org.labkey.api.util.Pair; +import org.labkey.api.util.element.Option; +import org.labkey.api.util.element.Select; +import org.labkey.specimen.report.SpecimenVisitReportParameters; + +import java.util.ArrayList; +import java.util.List; + +import static org.labkey.api.util.HtmlString.unsafe; + +/** + * User: brittp + * Created: Feb 1, 2008 + */ +public abstract class TypeReportFactory extends SpecimenVisitReportParameters +{ + @Override + public List> getAdditionalFormInputHtml(User user) + { + List> inputs = new ArrayList<>(super.getAdditionalFormInputHtml(user)); + + if (!SpecimenService.get().getRequestCustomizer().omitTypeGroupingsWhenReporting()) + { + Select.SelectBuilder builder = new Select.SelectBuilder(); + builder.name(PARAMS.typeLevel.name()); + + for (SpecimenTypeLevel level : SpecimenTypeLevel.values()) + { + builder.addOption(new Option.OptionBuilder() + .value(level.name()) + .label("Show results by: " + level.getLabel()) + .selected(getTypeLevelEnum() == level) + .build() + ); + } + inputs.add(new Pair<>("Type breakdown", unsafe(builder.toString()))); + } + + return inputs; + } +} diff --git a/specimen/src/org/labkey/specimen/report/specimentype/TypeSummaryReportFactory.java b/specimen/src/org/labkey/specimen/report/specimentype/TypeSummaryReportFactory.java new file mode 100644 index 00000000..c965a3e3 --- /dev/null +++ b/specimen/src/org/labkey/specimen/report/specimentype/TypeSummaryReportFactory.java @@ -0,0 +1,62 @@ +/* + * 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.specimen.report.specimentype; + +import org.labkey.api.data.SimpleFilter; +import org.labkey.api.specimen.SpecimenManager; +import org.labkey.specimen.report.SpecimenTypeVisitReport; +import org.labkey.specimen.report.SpecimenVisitReport; +import org.labkey.specimen.report.SpecimenVisitReportAction; +import org.labkey.specimen.actions.SpecimenReportActions; +import org.labkey.api.study.Visit; + +import java.util.Collections; +import java.util.List; + +/** + * User: brittp + * Created: Jan 24, 2008 1:38:06 PM + */ +public class TypeSummaryReportFactory extends TypeReportFactory +{ + @Override + public String getLabel() + { + return "Type Summary Report"; + } + + @Override + public String getReportType() + { + return "TypeSummary"; + } + + @Override + protected List createReports() + { + List visits = SpecimenManager.get().getVisitsWithSpecimens(getContainer(), getUser(), getCohort()); + SimpleFilter filter = new SimpleFilter(); + addBaseFilters(filter); + SpecimenTypeVisitReport report = new SpecimenTypeVisitReport("Summary", visits, filter, this); + return Collections.singletonList(report); + } + + @Override + public Class getAction() + { + return SpecimenReportActions.TypeSummaryReportAction.class; + } +} diff --git a/specimen/src/org/labkey/specimen/security/roles/AbstractSpecimenRole.java b/specimen/src/org/labkey/specimen/security/roles/AbstractSpecimenRole.java index c929ea7a..7fd976f0 100644 --- a/specimen/src/org/labkey/specimen/security/roles/AbstractSpecimenRole.java +++ b/specimen/src/org/labkey/specimen/security/roles/AbstractSpecimenRole.java @@ -39,7 +39,7 @@ protected AbstractSpecimenRole(String name, String description, Class getItems(ViewContext portalCtx) String iconBase = portalCtx.getContextPath() + "/study/tools/"; List items = new ArrayList<>(); - ActionURL vialSearchURL = PageFlowUtil.urlProvider(SpecimenUrls.class).getShowSearchURL(portalCtx.getContainer()); + ActionURL vialSearchURL = new ActionURL(ShowSearchAction.class, portalCtx.getContainer()); vialSearchURL.addParameter("showVials", true); items.add(new StudyToolsWebPart.Item("Vial Search", iconBase + "specimen_search.png", vialSearchURL)); @@ -41,7 +43,7 @@ protected List getItems(ViewContext portalCtx) if (portalCtx.getContainer().hasPermission(portalCtx.getUser(), RequestSpecimensPermission.class)) items.add(new StudyToolsWebPart.Item("New Request", iconBase + "specimen_request.png", PageFlowUtil.urlProvider(SpecimenUrls.class).getShowCreateSpecimenRequestURL(portalCtx.getContainer()))); } - items.add(new StudyToolsWebPart.Item("Specimen Reports", iconBase + "specimen_report.png", PageFlowUtil.urlProvider(SpecimenUrls.class).getAutoReportListURL(portalCtx.getContainer()))); + items.add(new StudyToolsWebPart.Item("Specimen Reports", iconBase + "specimen_report.png", new ActionURL(AutoReportListAction.class, portalCtx.getContainer()))); if (portalCtx.getContainer().hasPermission(portalCtx.getUser(), ManageStudyPermission.class)) items.add(new StudyToolsWebPart.Item("Settings", iconBase + "settings.png", PageFlowUtil.urlProvider(StudyUrls.class).getManageStudyURL(portalCtx.getContainer()))); diff --git a/specimen/src/org/labkey/specimen/view/autoReportList.jsp b/specimen/src/org/labkey/specimen/view/autoReportList.jsp index 1c1c2fc5..b347fab0 100644 --- a/specimen/src/org/labkey/specimen/view/autoReportList.jsp +++ b/specimen/src/org/labkey/specimen/view/autoReportList.jsp @@ -20,8 +20,6 @@ <%@ page import="org.labkey.api.data.Container" %> <%@ page import="org.labkey.api.query.CustomView" %> <%@ page import="org.labkey.api.security.User" %> -<%@ page import="org.labkey.api.specimen.actions.ReportConfigurationBean" %> -<%@ page import="org.labkey.api.specimen.report.SpecimenVisitReportParameters" %> <%@ page import="org.labkey.api.study.Cohort" %> <%@ page import="org.labkey.api.study.CohortFilter" %> <%@ page import="org.labkey.api.study.Params" %> @@ -35,6 +33,8 @@ <%@ page import="org.labkey.api.view.HttpView" %> <%@ page import="org.labkey.api.view.JspView" %> <%@ page import="org.labkey.api.view.template.FrameFactoryClassic" %> +<%@ page import="org.labkey.specimen.actions.ReportConfigurationBean" %> +<%@ page import="org.labkey.specimen.report.SpecimenVisitReportParameters" %> <%@ page import="java.util.List" %> <%@ page import="java.util.Map" %> <%@ taglib prefix="labkey" uri="http://www.labkey.org/taglib" %> diff --git a/specimen/src/org/labkey/specimen/view/configRequestabilityRules.jsp b/specimen/src/org/labkey/specimen/view/configRequestabilityRules.jsp index 6cde96c9..f75162be 100644 --- a/specimen/src/org/labkey/specimen/view/configRequestabilityRules.jsp +++ b/specimen/src/org/labkey/specimen/view/configRequestabilityRules.jsp @@ -16,10 +16,10 @@ */ %> <%@ page import="org.labkey.api.data.Container"%> +<%@ page import="org.labkey.api.specimen.importer.RequestabilityManager"%> <%@ page import="org.labkey.api.study.StudyUrls"%> -<%@ page import="org.labkey.api.view.ActionURL"%> +<%@ page import="org.labkey.api.view.ActionURL" %> <%@ page import="org.labkey.api.view.template.ClientDependencies" %> -<%@ page import="org.labkey.api.specimen.importer.RequestabilityManager" %> <%@ page extends="org.labkey.api.jsp.JspBase" %> <%@ taglib prefix="labkey" uri="http://www.labkey.org/taglib" %> <%! @@ -424,7 +424,7 @@ Ext.Msg.wait("Saving..."); Ext.Ajax.request({ - url : LABKEY.ActionURL.buildURL("specimen", "updateRequestabilityRules"), + url : LABKEY.ActionURL.buildURL("specimen2", "updateRequestabilityRules"), method : 'POST', success: saveComplete, failure: LABKEY.Utils.getCallbackWrapper(LABKEY.Utils.displayAjaxErrorResponse, this, true), diff --git a/specimen/src/org/labkey/specimen/view/customizeSpecimenReportWebPart.jsp b/specimen/src/org/labkey/specimen/view/customizeSpecimenReportWebPart.jsp index 1ad2b3da..8148dd2c 100644 --- a/specimen/src/org/labkey/specimen/view/customizeSpecimenReportWebPart.jsp +++ b/specimen/src/org/labkey/specimen/view/customizeSpecimenReportWebPart.jsp @@ -15,8 +15,8 @@ * limitations under the License. */ %> -<%@ page import="org.labkey.api.specimen.actions.ReportConfigurationBean" %> -<%@ page import="org.labkey.api.specimen.report.SpecimenVisitReportParameters" %> +<%@ page import="org.labkey.specimen.actions.ReportConfigurationBean" %> +<%@ page import="org.labkey.specimen.report.SpecimenVisitReportParameters" %> <%@ page import="org.labkey.api.view.HttpView" %> <%@ page import="org.labkey.api.view.JspView" %> <%@ page import="org.labkey.api.view.Portal.WebPart" %> diff --git a/specimen/src/org/labkey/specimen/view/groupMembers.jsp b/specimen/src/org/labkey/specimen/view/groupMembers.jsp new file mode 100644 index 00000000..624dd60b --- /dev/null +++ b/specimen/src/org/labkey/specimen/view/groupMembers.jsp @@ -0,0 +1,86 @@ +<% +/* + * 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.security.AuthenticationManager" %> +<%@ page import="org.labkey.api.security.User" %> +<%@ page import="org.labkey.specimen.actions.ShowGroupMembersAction" %> +<%@ page import="org.labkey.api.view.HttpView" %> +<%@ page import="org.labkey.api.view.JspView" %> +<%@ page extends="org.labkey.api.jsp.JspBase" %> +<%@ taglib prefix="labkey" uri="http://www.labkey.org/taglib" %> +<% + JspView me = (JspView) HttpView.currentView(); + ShowGroupMembersAction.GroupMembersBean bean = me.getModelBean(); +%> + + + + <%=generateReturnUrlFormField(bean.getReturnUrl())%> +<% + if (bean.getMembers() == null || bean.getMembers().length <= 0) + { + %>

This group currently has no members.

<% + } + else + { +%> +
+ Group members +
+ + + + + + <% + for (User member : bean.getMembers()) + { + %> + + + + + <% + } + %> +
RemoveEmail
<%= text(member.isActive() ? "" : "")%><%=h(member.getAutocompleteName(getContainer(), getUser()))%><%=text(member.isActive() ? "" : " (inactive)") %>
+
+ <% + } + %> +
+ +
+ Add New Members (enter one email address per line):
+
+ <%=AuthenticationManager.getStandardSendVerificationEmailsMessage()%>

+ + + <% + if (bean.getLocation() != null) + { + %> + + <% + } + %> + <%= button("Update Members").submit(true) %> +
+
+
\ No newline at end of file diff --git a/specimen/src/org/labkey/specimen/view/importSpecimens.jsp b/specimen/src/org/labkey/specimen/view/importSpecimens.jsp new file mode 100644 index 00000000..15f0bd44 --- /dev/null +++ b/specimen/src/org/labkey/specimen/view/importSpecimens.jsp @@ -0,0 +1,123 @@ +<% +/* + * 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. + */ +%> +<%@ taglib prefix="labkey" uri="http://www.labkey.org/taglib" %> +<%@ page import="org.labkey.api.specimen.pipeline.SpecimenArchive"%> +<%@ page import="org.labkey.api.view.HttpView"%> +<%@ page import="org.labkey.api.view.JspView"%> +<%@ page import="org.labkey.specimen.actions.SpecimenController2.ImportSpecimensBean" %> +<%@ page import="org.labkey.specimen.actions.SpecimenController2.SubmitSpecimenBatchImportAction" %> +<%@ page import="java.util.List" %> +<%@ page import="java.util.zip.ZipException" %> +<%@ page extends="org.labkey.api.jsp.JspBase" %> +<% + JspView me = (JspView) HttpView.currentView(); + ImportSpecimensBean bean = me.getModelBean(); + boolean hasError = !bean.getErrors().isEmpty(); + int archiveCount = bean.getArchives().size(); + String replaceOrMerge = bean.isDefaultMerge() ? "merge" : "replace"; + String mergeText = bean.isEditableSpecimens() ? "Insert new specimens. An error will result if any existing specimens are in the archive." : "Insert new specimens and update existing specimens."; + String replaceText = bean.isEditableSpecimens() ? "Replace all of the existing specimens. All edits will be lost." : "Replace all of the existing specimens."; +%> +<%= archiveCount %> specimen archive<%= text(archiveCount > 1 ? "s" : "") %> selected.

+<% + for (SpecimenArchive archive : bean.getArchives()) + { + String archiveName = archive.getDefinitionFile().getName(); + try + { + List entries = archive.getEntryDescriptions(getContainer()); +%> + Specimen archive <%= h(archiveName) %> contains the following files:

+ + + <% + int row = 0; + for (SpecimenArchive.EntryDescription entry : entries) + { + %> + + + + + + <% + } + %> +
FileSizeModified
<%= h(entry.getName()) %><%= entry.getSize() == 0 ? 0L : Math.max(1, entry.getSize() / 1000) %> kb<%=formatDateTime(entry.getDate())%>

+<% + } + catch (ZipException z) + { + hasError = true; +%> +

Specimen archive <%=h(archiveName)%> is corrupt and cannot be read.


+<% + } + } +%> + +
+ <% + if (hasError) + { + for (String error : bean.getErrors()) + { + %> +
<%= h(error) %> + <% + } + } + else + { +%> + + + <% + for (String file : bean.getFiles()) + { + %> + + <% + } + %> + <% + if (!bean.isNoSpecimens()) + { + %> + + +
+ + + <% + } + else + { + %> + + <% + } + %> +

+ + <%= button("Start Import").submit(true) %> + +<% + } + %> +

diff --git a/specimen/src/org/labkey/specimen/view/manageDefaultReqs.jsp b/specimen/src/org/labkey/specimen/view/manageDefaultReqs.jsp new file mode 100644 index 00000000..0fe82ccc --- /dev/null +++ b/specimen/src/org/labkey/specimen/view/manageDefaultReqs.jsp @@ -0,0 +1,233 @@ +<% +/* + * 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.specimen.actions.ManageReqsBean" %> +<%@ page import="org.labkey.api.specimen.model.SpecimenRequestActor" %> +<%@ page import="org.labkey.api.specimen.requirements.SpecimenRequestRequirement" %> +<%@ 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.HttpView" %> +<%@ page import="org.labkey.api.view.JspView" %> +<%@ page import="org.labkey.specimen.actions.SpecimenController2" %> +<%@ page import="org.labkey.specimen.actions.SpecimenController2.ManageDefaultReqsAction" %> +<%@ taglib prefix="labkey" uri="http://www.labkey.org/taglib" %> +<%@ page extends="org.labkey.api.jsp.JspBase" %> +<% + JspView me = (JspView) HttpView.currentView(); + ManageReqsBean bean = me.getModelBean(); + SpecimenRequestRequirement[] providerRequirements = bean.getProviderRequirements(); + SpecimenRequestRequirement[] originatingRequirements = bean.getOriginatorRequirements(); + SpecimenRequestRequirement[] receiverRequirements = bean.getReceiverRequirements(); + SpecimenRequestRequirement[] generalRequirements = bean.getGeneralRequirements(); + SpecimenRequestActor[] actors = bean.getActors(); + ActionURL deleteDefaultRequirement = urlFor(SpecimenController2.DeleteDefaultRequirementAction.class).addParameter("id",0); +%> + + + + + + + + + + <% + for (SpecimenRequestRequirement requirement : originatingRequirements) + { + %> + + + + + + <% + } + %> + + + + + + +
 ActorDescription
<%= link("Delete", deleteDefaultRequirement.replaceParameter("id", requirement.getRowId())).usePost() %><%= h(requirement.getActor().getLabel()) %><%= requirement.getDescription() != null ? h(requirement.getDescription()) : HtmlString.NBSP %>
  + + <%= button("Add Requirement").submit(true).onClick("return verifyNewRequirement('originator');") %>
+
+ + + + + + + + + <% + for (SpecimenRequestRequirement requirement : providerRequirements) + { + %> + + + + + + <% + } + %> + + + + + + +
 ActorDescription
<%= link("Delete", deleteDefaultRequirement.replaceParameter("id", requirement.getRowId())).usePost() %><%= h(requirement.getActor().getLabel()) %><%= requirement.getDescription() != null ? h(requirement.getDescription()) : HtmlString.NBSP %>
  + + <%= button("Add Requirement").submit(true).onClick("return verifyNewRequirement('provider');") %>
+
+ + + + + + + + + <% + for (SpecimenRequestRequirement requirement : receiverRequirements) + { + %> + + + + + + <% + } + %> + + + + + + +
 ActorDescription
<%= link("Delete", deleteDefaultRequirement.replaceParameter("id", requirement.getRowId())).usePost() %><%= h(requirement.getActor().getLabel()) %><%= requirement.getDescription() != null ? h(requirement.getDescription()) : HtmlString.NBSP %>
  + + <%= button("Add Requirement").submit(true).onClick("return verifyNewRequirement('receiver');") %>
+
+ + + + + + + + + <% + for (SpecimenRequestRequirement requirement : generalRequirements) + { + %> + + + + + + <% + } + %> + + + + + + +
 ActorDescription
<%= link("Delete", deleteDefaultRequirement.replaceParameter("id", requirement.getRowId())).usePost() %><%= h(requirement.getActor().getLabel()) %><%= requirement.getDescription() != null ? h(requirement.getDescription()) : HtmlString.NBSP %>
  + + <%= button("Add Requirement").submit(true).onClick("return verifyNewRequirement('general');") %>
+
+ +
+<%= link("manage study", urlProvider(StudyUrls.class).getManageStudyClass()) %> \ No newline at end of file diff --git a/specimen/src/org/labkey/specimen/view/manageDisplay.jsp b/specimen/src/org/labkey/specimen/view/manageDisplay.jsp new file mode 100644 index 00000000..7ed05243 --- /dev/null +++ b/specimen/src/org/labkey/specimen/view/manageDisplay.jsp @@ -0,0 +1,107 @@ +<% +/* + * 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.specimen.settings.DisplaySettings"%> +<%@ page import="org.labkey.api.study.StudyUrls"%> +<%@ page import="org.labkey.api.view.HttpView" %> +<%@ page import="org.labkey.api.view.JspView" %> +<%@ page import="org.labkey.specimen.actions.SpecimenController2.ManageDisplaySettingsAction" %> +<%@ taglib prefix="labkey" uri="http://www.labkey.org/taglib" %> +<%@ page extends="org.labkey.api.jsp.JspBase" %> +<% + JspView me = (JspView) HttpView.currentView(); + DisplaySettings bean = me.getModelBean(); +%> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Comments and Quality Control +
The specimen system can function in two modes: request mode, where vials are requested and requests are managed, + and comments/QC mode, where vial information is modified. Users with appropriate permissions can always manually switch modes.
Default mode: + +
Manual QC flagging/unflagging<%= helpPopup("Manual QC Flagging", "Vials are automatically flagged for QC " + + "at time of import if a vial's history contains conflicting information." + + "

Manual QC flagging/unflagging allows these states to be changed without updating the underlying specimen data. " + + "

Once a vial's QC state is set manually, it will no longer be updated automatically during the import process.", true)%>:

+ +
+ Low vial warnings +
If enabled, the specimen request system can display warning icons when one or zero vials of any primary specimen are available for request. The icon will appear next to all vials of that the primary specimen.
Display one available vial warning: + +
Display zero available vials warning: + +
+
+ <%= button("Save").submit(true) %>  + <%= button("Cancel").href(urlFor(urlProvider(StudyUrls.class).getManageStudyClass())) %> +
+
diff --git a/specimen/src/org/labkey/specimen/view/manageNotifications.jsp b/specimen/src/org/labkey/specimen/view/manageNotifications.jsp new file mode 100644 index 00000000..a09adcd2 --- /dev/null +++ b/specimen/src/org/labkey/specimen/view/manageNotifications.jsp @@ -0,0 +1,279 @@ +<% +/* + * 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.apache.commons.lang3.StringUtils"%> +<%@ page import="org.labkey.api.admin.AdminUrls"%> +<%@ page import="org.labkey.api.data.Container" %> +<%@ page import="org.labkey.api.security.SecurityUrls" %> +<%@ page import="org.labkey.api.security.permissions.AdminPermission" %> +<%@ page import="org.labkey.api.specimen.settings.RequestNotificationSettings" %> +<%@ page import="org.labkey.api.specimen.settings.RequestNotificationSettings.DefaultEmailNotifyEnum" %> +<%@ page import="org.labkey.api.specimen.settings.RequestNotificationSettings.SpecimensAttachmentEnum" %> +<%@ page import="org.labkey.api.specimen.view.SpecimenRequestNotificationEmailTemplate" %> +<%@ page import="org.labkey.api.study.StudyService" %> +<%@ 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.HttpView" %> +<%@ page import="org.labkey.api.view.JspView" %> +<%@ page import="org.labkey.specimen.actions.SpecimenController2.ManageNotificationsAction" %> +<%@ page extends="org.labkey.api.jsp.JspBase" %> +<%@ taglib prefix="labkey" uri="http://www.labkey.org/taglib" %> +<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> + + + +<% + JspView me = (JspView) HttpView.currentView(); + RequestNotificationSettings bean = me.getModelBean(); + Container container = getContainer(); + + ActionURL completionURLPrefix = urlProvider(SecurityUrls.class).getCompleteUserURL(container); + boolean newRequestNotifyChecked = ("POST".equalsIgnoreCase(getViewContext().getRequest().getMethod()) ? + bean.isNewRequestNotifyCheckbox() : StringUtils.isNotEmpty(bean.getNewRequestNotify())); + boolean ccChecked = ("POST".equalsIgnoreCase(getViewContext().getRequest().getMethod()) ? + bean.isCcCheckbox() : StringUtils.isNotEmpty(bean.getCc())); + DefaultEmailNotifyEnum defaultEmailNotifyEnum = (bean.getDefaultEmailNotifyEnum()); // Checking getMethod bot needed because one of the radio buttons will always POST + SpecimensAttachmentEnum specimensAttachmentEnum = (bean.getSpecimensAttachmentEnum()); +%> + + + + + + + + + + + + + + + + + + <% + boolean replyToCurrentUser = RequestNotificationSettings.REPLY_TO_CURRENT_USER_VALUE.equals(bean.getReplyTo()); + %> + + + + + + + + + + + + + + + + + + + + + + + + + + + + "> + + + + + + + + + + + + "> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
The specimen request system sends emails as requested by the specimen administrator. + Some properties of these email notifications can be configured here.
Notification emails will be sent from the specified reply-to address. + This is the address that will receive replies and error messages, so it should be a monitored address.
Reply-to Address:Replies to specimen request notifications should go to:
+ +
+ +
+ "> +
Specimen request emails have a configurable template, which controls + the subject line and body of the email. + <% if (getContainer().hasPermission(getUser(), AdminPermission.class)) { %><%= link("Edit Email Template", urlProvider(AdminUrls.class).getCustomizeEmailURL(getContainer(), SpecimenRequestNotificationEmailTemplate.class, getActionURL()))%><% } + else { %>You must have administrator permissions to edit the template.<% } %> +
All specimen request emails have the same subject line format. %requestId% may be used + to insert the specimen request's study-specific ID number. The default format for the subject line is: + <%= h(StudyService.get().getStudy(container).getLabel()) %>: [Subject Suffix] +
Subject Suffix: + +
Notification can be sent whenever a new specimen request is submitted.
+ +
Notify of new requests
(one per line):
+ +
Email addresses listed under "always CC" will receive a single copy of each email notification. + Please keep security issues in mind when adding users to this list.
+ +
Always CC
(one per line):
+ +
Each request requirement notification email allows you to specify which actors will receive the email. + The selection below indicates which actors will receive the email if the coordinator does not explicitly override.
Default Email Recipients: + +
+ +
+ +
In each notification email, a table of requested specimens can be included in the email body or as an attachment or not at all. To customize + that table, create a custom view called SpecimenEmail on the SpecimenDetail table.
Include Requested Specimens Table: + +
+ +
+ +
+ +
  + <%= button("Save").submit(true) %>  + <%= button("Cancel").href(urlProvider(StudyUrls.class).getManageStudyURL(container)) %> +
+
+ + + diff --git a/specimen/src/org/labkey/specimen/view/manageRepositorySettings.jsp b/specimen/src/org/labkey/specimen/view/manageRepositorySettings.jsp new file mode 100644 index 00000000..a685dc39 --- /dev/null +++ b/specimen/src/org/labkey/specimen/view/manageRepositorySettings.jsp @@ -0,0 +1,113 @@ +<% +/* + * 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.query.QueryUrls" %> +<%@ page import="org.labkey.api.specimen.settings.RepositorySettings" %> +<%@ page import="org.labkey.api.study.Study" %> +<%@ page import="org.labkey.api.study.StudyService" %> +<%@ page import="org.labkey.api.view.ActionURL" %> +<%@ page import="org.labkey.specimen.actions.SpecimenController2.ManageRepositorySettingsAction" %> +<%@ taglib prefix="labkey" uri="http://www.labkey.org/taglib" %> +<%@ page extends="org.labkey.api.jsp.JspBase" %> +<% + RepositorySettings settings = (RepositorySettings) getModelBean(); + Study study = StudyService.get().getStudy(settings.getContainer()); + ActionURL specimenDetailURL = new ActionURL(); + specimenDetailURL.setContainer(getContainer()); + specimenDetailURL.addParameter("schemaName", "study"); + specimenDetailURL.addParameter("query.queryName", "SpecimenDetail"); +%> + + +<% + if (study != null && !study.hasSourceStudy() && !study.isSnapshotStudy()) + { +%> + + + + + +
Repository Type
+
+ +
+
+ +
+
+ +
"> + + + + + + + +
Specimen Data
+
+ +
+
+ +
+
Specimen Requests
+
+ +
+
+ +
+
+
+
+
+ <%= button("Submit").submit(true) %> <%= button("Back").href(urlFor(ManageRepositorySettingsAction.class)).onClick("window.history.back();return false;") %> +
+
+<% + } + else + { +%> +

Specimen repository and request settings are not available for ancillary or published studies.

+<%= button("Back").href(urlFor(ManageRepositorySettingsAction.class)).onClick("window.history.back();return false;") %> +<% + } +%> \ No newline at end of file diff --git a/specimen/src/org/labkey/specimen/view/manageSpecimenWebPart.jsp b/specimen/src/org/labkey/specimen/view/manageSpecimenWebPart.jsp new file mode 100644 index 00000000..95291f5c --- /dev/null +++ b/specimen/src/org/labkey/specimen/view/manageSpecimenWebPart.jsp @@ -0,0 +1,239 @@ +<% +/* + * 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.specimen.actions.SpecimenController2.SpecimenWebPartForm" %> +<%@ page import="org.labkey.api.view.HttpView" %> +<%@ page import="org.labkey.api.view.JspView" %> +<%@ page import="org.labkey.api.view.template.ClientDependencies" %> +<%@ page extends="org.labkey.api.jsp.JspBase" %> +<%! + @Override + public void addClientDependencies(ClientDependencies dependencies) + { + dependencies.add("Ext4"); + } +%> +<% + JspView me = (JspView) HttpView.currentView(); + SpecimenWebPartForm bean = me.getModelBean(); + String[] grouping1 = bean.getGrouping1(); + String[] grouping2 = bean.getGrouping2(); + String[] columns = bean.getColumns(); +%> +
+

You can specify one or two specimen grouping sections on the specimen web part that allow specimens to be easily viewed by those groupings. +

+
+
+ + diff --git a/specimen/src/org/labkey/specimen/view/selectSpecimenProvider.jsp b/specimen/src/org/labkey/specimen/view/selectSpecimenProvider.jsp new file mode 100644 index 00000000..e53fedf4 --- /dev/null +++ b/specimen/src/org/labkey/specimen/view/selectSpecimenProvider.jsp @@ -0,0 +1,48 @@ +<% +/* + * 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.specimen.actions.SelectSpecimenProviderBean"%> +<%@ page import="org.labkey.api.specimen.location.LocationImpl" %> +<%@ page import="org.labkey.api.view.HttpView" %> +<%@ page import="org.labkey.api.view.JspView" %> +<%@ taglib prefix="labkey" uri="http://www.labkey.org/taglib" %> +<%@ page extends="org.labkey.api.jsp.JspBase" %> +<% + JspView me = (JspView) HttpView.currentView(); + SelectSpecimenProviderBean bean = me.getModelBean(); +%> + +

Vials from the selected specimens can be shipped to you from multiple locations. Please select your preferred location:

+ +<%= h(bean.getSourceForm().getHiddenFormInputs(getViewContext())) %> +

+ +

+

+ <%= generateBackButton() %> + <%= button("Select").submit(true) %> +

+
\ No newline at end of file diff --git a/specimen/src/org/labkey/specimen/view/specimenVisitReport.jsp b/specimen/src/org/labkey/specimen/view/specimenVisitReport.jsp index db30b3e0..d233858e 100644 --- a/specimen/src/org/labkey/specimen/view/specimenVisitReport.jsp +++ b/specimen/src/org/labkey/specimen/view/specimenVisitReport.jsp @@ -16,9 +16,9 @@ */ %> <%@ page import="org.apache.commons.lang3.StringUtils"%> -<%@ page import="org.labkey.api.specimen.report.SpecimenReportTitle" %> -<%@ page import="org.labkey.api.specimen.report.SpecimenVisitReport" %> -<%@ page import="org.labkey.api.specimen.report.SpecimenVisitReportParameters" %> +<%@ page import="org.labkey.specimen.report.SpecimenReportTitle" %> +<%@ page import="org.labkey.specimen.report.SpecimenVisitReport" %> +<%@ page import="org.labkey.specimen.report.SpecimenVisitReportParameters" %> <%@ page import="org.labkey.api.study.SpecimenService" %> <%@ page import="org.labkey.api.study.Study" %> <%@ page import="org.labkey.api.study.StudyService" %> diff --git a/specimen/src/org/labkey/specimen/view/uploadSimpleSpecimens.jsp b/specimen/src/org/labkey/specimen/view/uploadSimpleSpecimens.jsp new file mode 100644 index 00000000..19780236 --- /dev/null +++ b/specimen/src/org/labkey/specimen/view/uploadSimpleSpecimens.jsp @@ -0,0 +1,154 @@ +<% +/* + * 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.study.Study" %> +<%@ page import="org.labkey.api.study.StudyService" %> +<%@ 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.specimen.actions.ShowUploadSpecimensAction" %> +<%@ page import="org.labkey.specimen.actions.SpecimenController2.GetSpecimenExcelAction" %> +<%@ page extends="org.labkey.api.jsp.JspBase" %> +<%@ taglib prefix="labkey" uri="http://www.labkey.org/taglib" %> +<% + JspView me = (JspView) HttpView.currentView(); + ShowUploadSpecimensAction.UploadSpecimensForm bean = me.getModelBean(); + Container c = getContainer(); + Study study = StudyService.get().getStudy(c); +%> + +Use this form to insert or update specimens in the repository.
+<%=link("Download a template workbook", GetSpecimenExcelAction.class)%>
+ +
<%=link("Show Expected Data Fields").onClick("document.getElementById('expectedDataFields').style.display = 'block'; document.getElementById('showExpectedDataFieldsDiv').style.display = 'none'; return false;").id("showExpectedDataFieldsLink") %>
+ + +
+Paste data in the area below + +
+ +<% + if (!bean.isNoSpecimens()) + { +%> +

+ + +
+ + +<% + } + else + { +%> + +<% + } +%> +

+ <%= button("Submit").submit(true) %> <%= generateBackButton("Cancel") %> + + diff --git a/specimen/src/org/labkey/specimen/view/viewRequestsHeader.jsp b/specimen/src/org/labkey/specimen/view/viewRequestsHeader.jsp new file mode 100644 index 00000000..d62332a2 --- /dev/null +++ b/specimen/src/org/labkey/specimen/view/viewRequestsHeader.jsp @@ -0,0 +1,58 @@ +<% +/* + * 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.specimen.SpecimenRequestStatus" %> +<%@ page import="org.labkey.api.specimen.actions.ViewRequestsHeaderBean" %> +<%@ page import="org.labkey.api.view.ActionURL" %> +<%@ page import="org.labkey.api.view.HttpView" %> +<%@ page import="org.labkey.api.view.JspView" %> +<%@ page import="org.labkey.api.view.ViewContext" %> +<%@ page extends="org.labkey.api.jsp.JspBase" %> +<% + JspView me = (JspView) HttpView.currentView(); + ViewContext context = getViewContext(); + ViewRequestsHeaderBean bean = me.getModelBean(); + ActionURL userLink = context.cloneActionURL(); +%> +<%= link("All User Requests", userLink.deleteParameter(ViewRequestsHeaderBean.PARAM_CREATEDBY)) %> +<%= link("My Requests", userLink.replaceParameter(ViewRequestsHeaderBean.PARAM_CREATEDBY, + getUser().getDisplayName(getUser()))) %> +Filter by status: +<% + String userFilter = getActionURL().getParameter(ViewRequestsHeaderBean.PARAM_CREATEDBY); + if (userFilter != null) + { +%> +Showing requests from user <%= h(userFilter) %> +<% + } +%> diff --git a/specimen/webapp/WEB-INF/specimen/specimenContext.xml b/specimen/webapp/WEB-INF/specimen/specimenContext.xml index 55da46f9..bc6713ba 100644 --- a/specimen/webapp/WEB-INF/specimen/specimenContext.xml +++ b/specimen/webapp/WEB-INF/specimen/specimenContext.xml @@ -16,7 +16,7 @@ - + From b1cdfd20d4c8e599f32fdde26c2296dbc754e5c9 Mon Sep 17 00:00:00 2001 From: labkey-matthewb Date: Thu, 3 Jun 2021 11:59:04 -0700 Subject: [PATCH 335/494] AuditTypeProvider.getDomainKind() perf Original PR: https://github.com/LabKey/platform/pull/2314 * optimize AuditTypeProvider.getDomainKind() --- .../org/labkey/specimen/SpecimenCommentAuditProvider.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/specimen/src/org/labkey/specimen/SpecimenCommentAuditProvider.java b/specimen/src/org/labkey/specimen/SpecimenCommentAuditProvider.java index f4a6bbb4..cd3f0c68 100644 --- a/specimen/src/org/labkey/specimen/SpecimenCommentAuditProvider.java +++ b/specimen/src/org/labkey/specimen/SpecimenCommentAuditProvider.java @@ -64,10 +64,9 @@ public class SpecimenCommentAuditProvider extends AbstractAuditTypeProvider impl FieldKey.fromParts(COLUMN_NAME_COMMENT) ); - @Override - protected AbstractAuditDomainKind getDomainKind() + public SpecimenCommentAuditProvider() { - return new SpecimenCommentAuditDomainKind(); + super(new SpecimenCommentAuditDomainKind()); } @Override From ba04c3ec1ae77a5e30e9cce4e427ca82fb2e1a31 Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Thu, 3 Jun 2021 12:02:53 -0700 Subject: [PATCH 336/494] Move more code to specimen module Original PR: https://github.com/LabKey/platform/pull/2318 --- .../org/labkey/specimen/SpecimenModule.java | 25 +- .../specimen/actions/ManageReqsBean.java | 58 ++ .../specimen/actions/ShowSearchAction.java | 1 - .../specimen/actions/SpecimenController2.java | 698 +++++++++++++++++- .../specimen/actions/SpecimenSearchBean.java | 187 +++++ .../actions/ViewRequestsHeaderBean.java | 38 + .../importer/SpecimenSettingsImporter.java | 2 +- .../specimen/pipeline/SpecimenArchive.java | 108 +++ .../query/SpecimenEventQueryView.java | 53 ++ .../query/SpecimenRequestQueryView.java | 222 ++++++ .../permissions/LockSpecimensPermission.java | 34 + .../ManageDisplaySettingsPermission.java | 34 + .../ManageNotificationsPermission.java | 34 + .../ManageRequestRequirementsPermission.java | 34 + .../roles/SpecimenCoordinatorRole.java | 8 +- .../specimen/view/SpecimenSearchWebPart.java | 39 + .../view/SpecimenSearchWebPartFactory.java | 1 - .../view/SpecimenToolsWebPartFactory.java | 6 +- .../labkey/specimen/view/importSpecimens.jsp | 4 +- .../specimen/view/manageDefaultReqs.jsp | 6 +- .../labkey/specimen/view/requestSpecimens.jsp | 244 ++++++ .../src/org/labkey/specimen/view/search.jsp | 2 +- .../specimen/view/viewRequestsHeader.jsp | 2 +- .../writer/SpecimenSettingsWriter.java | 2 +- 24 files changed, 1808 insertions(+), 34 deletions(-) create mode 100644 specimen/src/org/labkey/specimen/actions/ManageReqsBean.java create mode 100644 specimen/src/org/labkey/specimen/actions/SpecimenSearchBean.java create mode 100644 specimen/src/org/labkey/specimen/actions/ViewRequestsHeaderBean.java create mode 100644 specimen/src/org/labkey/specimen/pipeline/SpecimenArchive.java create mode 100644 specimen/src/org/labkey/specimen/query/SpecimenEventQueryView.java create mode 100644 specimen/src/org/labkey/specimen/query/SpecimenRequestQueryView.java create mode 100644 specimen/src/org/labkey/specimen/security/permissions/LockSpecimensPermission.java create mode 100644 specimen/src/org/labkey/specimen/security/permissions/ManageDisplaySettingsPermission.java create mode 100644 specimen/src/org/labkey/specimen/security/permissions/ManageNotificationsPermission.java create mode 100644 specimen/src/org/labkey/specimen/security/permissions/ManageRequestRequirementsPermission.java create mode 100644 specimen/src/org/labkey/specimen/view/SpecimenSearchWebPart.java create mode 100644 specimen/src/org/labkey/specimen/view/requestSpecimens.jsp diff --git a/specimen/src/org/labkey/specimen/SpecimenModule.java b/specimen/src/org/labkey/specimen/SpecimenModule.java index a47a0aa5..e66da734 100644 --- a/specimen/src/org/labkey/specimen/SpecimenModule.java +++ b/specimen/src/org/labkey/specimen/SpecimenModule.java @@ -62,6 +62,7 @@ import org.labkey.specimen.writer.SpecimenArchiveWriter; import org.labkey.specimen.writer.SpecimenSettingsWriter; import org.labkey.specimen.writer.SpecimenWriter; +import org.springframework.web.servlet.mvc.Controller; import java.util.Collection; import java.util.Collections; @@ -158,12 +159,6 @@ public ActionURL getAutoReportListURL(Container c) return new ActionURL(SpecimenController2.AutoReportListAction.class, c); } - @Override - public ActionURL getCompleteSpecimenURL(Container c, String type) - { - return new ActionURL(SpecimenController2.CompleteSpecimenAction.class, c).addParameter("type", type); - } - @Override public ActionURL getShowSearchURL(Container c, boolean showVials) { @@ -217,6 +212,24 @@ public ActionURL getManageDefaultReqsSettingsURL(Container c) { return new ActionURL(SpecimenController2.ManageDefaultReqsAction.class, c); } + + @Override + public Class getShowCreateSpecimenRequestActionClass() + { + return SpecimenController2.ShowCreateSpecimenRequestAction.class; + } + + @Override + public Class getShowAPICreateSpecimenRequestActionClass() + { + return SpecimenController2.ShowAPICreateSpecimenRequestAction.class; + } + + @Override + public Class getExtendedSpecimenRequestActionClass() + { + return SpecimenController2.ExtendedSpecimenRequestAction.class; + } }); } diff --git a/specimen/src/org/labkey/specimen/actions/ManageReqsBean.java b/specimen/src/org/labkey/specimen/actions/ManageReqsBean.java new file mode 100644 index 00000000..7e47f832 --- /dev/null +++ b/specimen/src/org/labkey/specimen/actions/ManageReqsBean.java @@ -0,0 +1,58 @@ +package org.labkey.specimen.actions; + +import org.labkey.api.data.Container; +import org.labkey.api.security.User; +import org.labkey.api.specimen.model.SpecimenRequestActor; +import org.labkey.api.specimen.requirements.RequirementProvider; +import org.labkey.api.specimen.requirements.SpecimenRequestRequirement; +import org.labkey.api.specimen.requirements.SpecimenRequestRequirementProvider; +import org.labkey.api.specimen.requirements.SpecimenRequestRequirementType; + +public class ManageReqsBean +{ + private final SpecimenRequestRequirement[] _providerRequirements; + private final SpecimenRequestRequirement[] _receiverRequirements; + private final SpecimenRequestRequirement[] _generalRequirements; + private final SpecimenRequestRequirement[] _originatorRequirements; + private final SpecimenRequestActor[] _actors; + + public ManageReqsBean(User user, Container container) + { + RequirementProvider provider = + SpecimenRequestRequirementProvider.get(); + _originatorRequirements = provider.getDefaultRequirements(container, + SpecimenRequestRequirementType.ORIGINATING_SITE); + _providerRequirements = provider.getDefaultRequirements(container, + SpecimenRequestRequirementType.PROVIDING_SITE); + _receiverRequirements = provider.getDefaultRequirements(container, + SpecimenRequestRequirementType.RECEIVING_SITE); + _generalRequirements = provider.getDefaultRequirements(container, + SpecimenRequestRequirementType.NON_SITE_BASED); + _actors = provider.getActors(container); + } + + public SpecimenRequestRequirement[] getGeneralRequirements() + { + return _generalRequirements; + } + + public SpecimenRequestRequirement[] getReceiverRequirements() + { + return _receiverRequirements; + } + + public SpecimenRequestRequirement[] getProviderRequirements() + { + return _providerRequirements; + } + + public SpecimenRequestRequirement[] getOriginatorRequirements() + { + return _originatorRequirements; + } + + public SpecimenRequestActor[] getActors() + { + return _actors; + } +} diff --git a/specimen/src/org/labkey/specimen/actions/ShowSearchAction.java b/specimen/src/org/labkey/specimen/actions/ShowSearchAction.java index 587e7e73..c9f42d06 100644 --- a/specimen/src/org/labkey/specimen/actions/ShowSearchAction.java +++ b/specimen/src/org/labkey/specimen/actions/ShowSearchAction.java @@ -19,7 +19,6 @@ import org.labkey.api.data.CompareType; import org.labkey.api.security.RequiresPermission; import org.labkey.api.security.permissions.ReadPermission; -import org.labkey.api.specimen.SpecimenSearchBean; import org.labkey.api.study.SpecimenUrls; import org.labkey.api.study.Study; import org.labkey.api.study.StudyService; diff --git a/specimen/src/org/labkey/specimen/actions/SpecimenController2.java b/specimen/src/org/labkey/specimen/actions/SpecimenController2.java index 7bd53f77..104952fd 100644 --- a/specimen/src/org/labkey/specimen/actions/SpecimenController2.java +++ b/specimen/src/org/labkey/specimen/actions/SpecimenController2.java @@ -10,6 +10,7 @@ import org.labkey.api.action.HasViewContext; 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.attachments.AttachmentParent; @@ -17,6 +18,9 @@ import org.labkey.api.data.ActionButton; import org.labkey.api.data.BeanViewForm; import org.labkey.api.data.Container; +import org.labkey.api.data.DataRegion; +import org.labkey.api.data.DataRegionSelection; +import org.labkey.api.data.DbScope; import org.labkey.api.data.ExcelColumn; import org.labkey.api.data.SimpleFilter; import org.labkey.api.module.FolderType; @@ -24,32 +28,35 @@ import org.labkey.api.pipeline.PipelineService; import org.labkey.api.pipeline.PipelineStatusUrls; import org.labkey.api.pipeline.browse.PipelinePathForm; +import org.labkey.api.query.ValidationException; import org.labkey.api.security.RequiresPermission; import org.labkey.api.security.RequiresSiteAdmin; import org.labkey.api.security.User; import org.labkey.api.security.ValidEmail; import org.labkey.api.security.permissions.AdminPermission; import org.labkey.api.security.permissions.ReadPermission; +import org.labkey.api.specimen.AmbiguousLocationException; +import org.labkey.api.specimen.RequestedSpecimens; +import org.labkey.api.specimen.SpecimenManager; import org.labkey.api.specimen.SpecimenManagerNew; +import org.labkey.api.specimen.SpecimenRequestException; import org.labkey.api.specimen.SpecimenRequestManager; -import org.labkey.api.specimen.SpecimenSearchWebPart; +import org.labkey.api.specimen.SpecimenSchema; import org.labkey.api.specimen.Vial; +import org.labkey.api.specimen.actions.HiddenFormInputGenerator; import org.labkey.api.specimen.actions.IdForm; -import org.labkey.api.specimen.actions.ManageReqsBean; +import org.labkey.api.specimen.actions.SelectSpecimenProviderBean; import org.labkey.api.specimen.actions.SpecimenEventBean; -import org.labkey.api.specimen.actions.ViewRequestsHeaderBean; import org.labkey.api.specimen.importer.RequestabilityManager; import org.labkey.api.specimen.importer.SimpleSpecimenImporter; +import org.labkey.api.specimen.location.LocationManager; +import org.labkey.api.specimen.model.ExtendedSpecimenRequestView; import org.labkey.api.specimen.model.SpecimenRequestEvent; -import org.labkey.api.specimen.pipeline.SpecimenArchive; -import org.labkey.api.specimen.query.SpecimenEventQueryView; -import org.labkey.api.specimen.query.SpecimenRequestQueryView; +import org.labkey.api.specimen.query.SpecimenQueryView; +import org.labkey.api.specimen.requirements.SpecimenRequest; import org.labkey.api.specimen.requirements.SpecimenRequestRequirement; import org.labkey.api.specimen.requirements.SpecimenRequestRequirementProvider; import org.labkey.api.specimen.requirements.SpecimenRequestRequirementType; -import org.labkey.api.specimen.security.permissions.ManageDisplaySettingsPermission; -import org.labkey.api.specimen.security.permissions.ManageNotificationsPermission; -import org.labkey.api.specimen.security.permissions.ManageRequestRequirementsPermission; import org.labkey.api.specimen.security.permissions.ManageRequestSettingsPermission; import org.labkey.api.specimen.security.permissions.RequestSpecimensPermission; import org.labkey.api.specimen.settings.DisplaySettings; @@ -58,11 +65,16 @@ import org.labkey.api.specimen.settings.SettingsManager; import org.labkey.api.specimen.view.SpecimenWebPart; import org.labkey.api.study.MapArrayExcelWriter; +import org.labkey.api.study.SpecimenService; import org.labkey.api.study.SpecimenUrls; import org.labkey.api.study.Study; +import org.labkey.api.study.StudyInternalService; import org.labkey.api.study.StudyService; import org.labkey.api.study.StudyUrls; import org.labkey.api.study.security.permissions.ManageStudyPermission; +import org.labkey.api.util.ConfigurationException; +import org.labkey.api.util.ExceptionUtil; +import org.labkey.api.util.GUID; import org.labkey.api.util.HelpTopic; import org.labkey.api.util.PageFlowUtil; import org.labkey.api.util.Pair; @@ -78,17 +90,31 @@ import org.labkey.api.view.ViewBackgroundInfo; import org.labkey.api.view.ViewContext; import org.labkey.api.view.WebPartView; +import org.labkey.specimen.pipeline.SpecimenArchive; import org.labkey.specimen.pipeline.SpecimenBatch; +import org.labkey.specimen.query.SpecimenEventQueryView; +import org.labkey.specimen.query.SpecimenRequestQueryView; +import org.labkey.specimen.security.permissions.ManageDisplaySettingsPermission; +import org.labkey.specimen.security.permissions.ManageNotificationsPermission; +import org.labkey.specimen.security.permissions.ManageRequestRequirementsPermission; +import org.labkey.specimen.view.SpecimenSearchWebPart; import org.springframework.validation.BindException; import org.springframework.validation.Errors; import org.springframework.web.servlet.ModelAndView; +import javax.servlet.http.HttpServletRequest; import java.io.File; import java.io.IOException; +import java.sql.SQLException; +import java.sql.Timestamp; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; import java.util.Collections; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; // TEMPORARY: Move specimen actions from study SpecimenController to here. Once all actions are moved, we'll rename this. public class SpecimenController2 extends SpringActionController @@ -739,7 +765,7 @@ public ModelAndView getView(Object o, BindException errors) grid.setShowDetailsColumn(false); if (getContainer().hasPermission(getUser(), RequestSpecimensPermission.class)) { - ActionButton insertButton = new ActionButton(urlProvider(SpecimenUrls.class).getShowCreateSpecimenRequestAction(getContainer()), "Create New Request", ActionButton.Action.LINK); + ActionButton insertButton = new ActionButton(new ActionURL(ShowCreateSpecimenRequestAction.class, getContainer()), "Create New Request", ActionButton.Action.LINK); grid.setButtons(Collections.singletonList(insertButton)); } else @@ -1411,4 +1437,656 @@ private List getAjaxCompletions(Study study) return completions; } + public static List getSpecimensFromRowIds(long[] requestedSampleIds, Container container, User user) + { + List requestedVials = null; + + if (requestedSampleIds != null) + { + List vials = new ArrayList<>(); + for (long requestedSampleId : requestedSampleIds) + { + Vial current = SpecimenManagerNew.get().getVial(container, user, requestedSampleId); + if (current != null) + vials.add(current); + } + requestedVials = vials; + } + + return requestedVials; + } + + private static long[] toLongArray(Collection intStrings) + { + if (intStrings == null) + return null; + long[] converted = new long[intStrings.size()]; + int index = 0; + for (String intString : intStrings) + converted[index++] = Long.parseLong(intString); + return converted; + } + + public static List getSpecimensFromRowIds(Collection ids, Container container, User user) + { + return getSpecimensFromRowIds(toLongArray(ids), container, user); + } + + public List getSpecimensFromPost(boolean fromGroupedView, boolean onlyAvailable) + { + Set formValues = null; + if ("POST".equalsIgnoreCase(getViewContext().getRequest().getMethod())) + formValues = DataRegionSelection.getSelected(getViewContext(), true); + + if (formValues == null || formValues.isEmpty()) + return null; + + List selectedVials; + if (fromGroupedView) + { + Map> keyToVialMap = + SpecimenManagerNew.get().getVialsForSpecimenHashes(getContainer(), getUser(), formValues, onlyAvailable); + List vials = new ArrayList<>(); + for (List vialList : keyToVialMap.values()) + vials.addAll(vialList); + selectedVials = new ArrayList<>(vials); + } + else + selectedVials = getSpecimensFromRowIds(formValues, getContainer(), getUser()); + return selectedVials; + } + + public static class CreateSpecimenRequestForm extends ReturnUrlForm implements HiddenFormInputGenerator + { + public enum PARAMS + { + returnUrl, + extendedRequestUrl, + ignoreReturnUrl + } + + private String[] _inputs; + private int _destinationLocation; + private long[] _specimenRowIds; + private boolean[] _required; + private boolean _fromGroupedView; + private Integer _preferredLocation; + private boolean _ignoreReturnUrl; + private boolean _extendedRequestUrl; + private String[] _specimenIds; + + @Override + public String getHiddenFormInputs(ViewContext ctx) + { + StringBuilder builder = new StringBuilder(); + if (_inputs != null) + { + for (String input : _inputs) + builder.append("\n"); + } + if (_destinationLocation != 0) + builder.append("\n"); + if (getReturnUrl() != null) + builder.append("\n"); + if (_specimenRowIds != null) + { + for (long specimenId : _specimenRowIds) + builder.append("\n"); + } + else + { + String dataRegionSelectionKey = ctx.getRequest().getParameter(DataRegionSelection.DATA_REGION_SELECTION_KEY); + if (dataRegionSelectionKey != null) + { + builder.append("\n"); + Set specimenFormValues = DataRegionSelection.getSelected(ctx, false); + for (String formValue : specimenFormValues) + { + builder.append("\n"); + } + } + } + + if (_required != null) + { + for (boolean required : _required) + builder.append("\n"); + } + + if (_specimenIds != null) + { + for (String specimenId : _specimenIds) + builder.append("\n"); + } + + builder.append("\n"); + return builder.toString(); + } + + public long[] getSpecimenRowIds() + { + return _specimenRowIds; + } + + public void setSpecimenRowIds(long[] specimenRowIds) + { + _specimenRowIds = specimenRowIds; + } + + public int getDestinationLocation() + { + return _destinationLocation; + } + + public void setDestinationLocation(int destinationLocation) + { + _destinationLocation = destinationLocation; + } + + public String[] getInputs() + { + return _inputs; + } + + public void setInputs(String[] inputs) + { + _inputs = inputs; + } + + public boolean[] getRequired() + { + return _required; + } + + public void setRequired(boolean[] required) + { + _required = required; + } + + public boolean isFromGroupedView() + { + return _fromGroupedView; + } + + public void setFromGroupedView(boolean fromGroupedView) + { + _fromGroupedView = fromGroupedView; + } + + public Integer getPreferredLocation() + { + return _preferredLocation; + } + + public void setPreferredLocation(Integer preferredLocation) + { + _preferredLocation = preferredLocation; + } + + public boolean isIgnoreReturnUrl() + { + return _ignoreReturnUrl; + } + + public void setIgnoreReturnUrl(boolean ignoreReturnUrl) + { + _ignoreReturnUrl = ignoreReturnUrl; + } + + public boolean isExtendedRequestUrl() + { + return _extendedRequestUrl; + } + + public void setExtendedRequestUrl(boolean extendedRequestUrl) + { + _extendedRequestUrl = extendedRequestUrl; + } + + public String[] getSpecimenIds() + { + return _specimenIds; + } + + public void setSpecimenIds(String[] specimenIds) + { + _specimenIds = specimenIds; + } + + public RequestedSpecimens getSelectedSpecimens(ViewContext ctx) throws AmbiguousLocationException + { + Container container = ctx.getContainer(); + User user = ctx.getUser(); + HttpServletRequest request = ctx.getRequest(); + + // first check for explicitly listed specimen row ids (this is the case when posting the final + // specimen request form): + List requestedSpecimens = getSpecimensFromRowIds(getSpecimenRowIds(), container, user); + if (requestedSpecimens != null && requestedSpecimens.size() > 0) + return new RequestedSpecimens(requestedSpecimens); + + Set ids; + if ("post".equalsIgnoreCase(request.getMethod()) && + (request.getParameter(DataRegionSelection.DATA_REGION_SELECTION_KEY) != null)) + { + ids = DataRegionSelection.getSelected(ctx, null, true); + if (isFromGroupedView()) + return SpecimenRequestManager.get().getRequestableBySpecimenHash(container, user, ids, getPreferredLocation()); + else + return getRequestableByVialRowIds(ids, container, user); + } + else if (_specimenIds != null && _specimenIds.length > 0) + { + ids = new HashSet<>(); + Collections.addAll(ids, _specimenIds); + if (isFromGroupedView()) + return SpecimenRequestManager.get().getRequestableBySpecimenHash(container, user, ids, getPreferredLocation()); + else + return getRequestableByVialGlobalUniqueIds(ids, container, user); + } + else + return null; + } + + private RequestedSpecimens getRequestableByVialRowIds(Set rowIds, Container container, User user) + { + Set ids = new HashSet<>(); + Arrays.stream(toLongArray(rowIds)).forEach(ids::add); + List requestedSpecimens = SpecimenManagerNew.get().getRequestableVials(container, user, ids); + return new RequestedSpecimens(requestedSpecimens); + } + + private RequestedSpecimens getRequestableByVialGlobalUniqueIds(Set globalUniqueIds, Container container, User user) + { + List requestedVials = null; + + if (globalUniqueIds != null) + { + List vials = new ArrayList<>(); + for (String globalUniqueId : globalUniqueIds) + { + Vial match = SpecimenManagerNew.get().getVial(container, user, globalUniqueId); + if (match != null) + vials.add(match); + } + requestedVials = new ArrayList<>(vials); + } + return new RequestedSpecimens(requestedVials); + } + } + + public abstract static class SpecimensViewBean + { + protected SpecimenQueryView _specimenQueryView; + protected List _vials; + + public SpecimensViewBean(ViewContext context, List vials, boolean showHistoryLinks, + boolean showRecordSelectors, boolean disableLowVialIndicators, boolean restrictRecordSelectors) + { + _vials = vials; + if (vials != null && vials.size() > 0) + { + _specimenQueryView = SpecimenQueryView.createView(context, vials, SpecimenQueryView.ViewType.VIALS); + _specimenQueryView.setShowHistoryLinks(showHistoryLinks); + _specimenQueryView.setShowRecordSelectors(showRecordSelectors); + _specimenQueryView.setDisableLowVialIndicators(disableLowVialIndicators); + _specimenQueryView.setRestrictRecordSelectors(restrictRecordSelectors); + } + } + + public SpecimenQueryView getSpecimenQueryView() + { + return _specimenQueryView; + } + + public List getVials() + { + return _vials; + } + } + + public static class NewRequestBean extends SpecimensViewBean + { + private final Container _container; + private final SpecimenRequestManager.SpecimenRequestInput[] _inputs; + private final String[] _inputValues; + private final int _selectedSite; + private final BindException _errors; + private final ActionURL _returnUrl; + + public NewRequestBean(ViewContext context, RequestedSpecimens requestedSpecimens, CreateSpecimenRequestForm form, BindException errors) throws SQLException + { + super(context, requestedSpecimens != null ? requestedSpecimens.getVials() : null, false, false, false, false); + _errors = errors; + _inputs = SpecimenRequestManager.get().getNewSpecimenRequestInputs(context.getContainer()); + _selectedSite = form.getDestinationLocation(); + _inputValues = form.getInputs(); + _container = context.getContainer(); + _returnUrl = form.getReturnActionURL(); + } + + public SpecimenRequestManager.SpecimenRequestInput[] getInputs() + { + return _inputs; + } + + public String getValue(int inputIndex) throws ValidationException + { + if (_inputValues != null && inputIndex < _inputValues.length && _inputValues[inputIndex] != null) + return _inputValues[inputIndex]; + if (_inputs[inputIndex].isRememberSiteValue() && _selectedSite > 0) + return _inputs[inputIndex].getDefaultSiteValues(_container).get(_selectedSite); + return ""; + } + + public int getSelectedSite() + { + return _selectedSite; + } + + public BindException getErrors() + { + return _errors; + } + + public ActionURL getReturnUrl() + { + return _returnUrl; + } + } + + @RequiresPermission(RequestSpecimensPermission.class) + public class HandleCreateSpecimenRequestAction extends FormViewAction + { + private SpecimenRequest _specimenRequest; + + @Override + public ModelAndView getView(CreateSpecimenRequestForm form, boolean reshow, BindException errors) throws Exception + { + return getCreateSpecimenRequestView(form, errors); + } + + @Override + public void addNavTrail(NavTree root) + { + addSpecimenRequestsNavTrail(root); + root.addChild("New Specimen Request"); + } + + @Override + public void validateCommand(CreateSpecimenRequestForm form, Errors errors) + { + boolean missingRequiredInput = false; + if (form.getDestinationLocation() <= 0) + missingRequiredInput = true; + + for (int i = 0; i < form.getInputs().length && !missingRequiredInput; i++) + { + if (form.getRequired()[i] && (form.getInputs()[i] == null || form.getInputs()[i].length() == 0)) + missingRequiredInput = true; + } + + if (missingRequiredInput) + errors.reject(null, "Please provide all required input."); + } + + @Override + public boolean handlePost(CreateSpecimenRequestForm form, BindException errors) throws Exception + { + ensureSpecimenRequestsConfigured(true); + + String[] inputs = form.getInputs(); + long[] specimenIds = form.getSpecimenRowIds(); + StringBuilder comments = new StringBuilder(); + SpecimenRequestManager.SpecimenRequestInput[] expectedInputs = + SpecimenRequestManager.get().getNewSpecimenRequestInputs(getContainer()); + if (inputs.length != expectedInputs.length) + throw new IllegalStateException("Expected a form element for each input."); + + for (int i = 0; i < expectedInputs.length; i++) + { + SpecimenRequestManager.SpecimenRequestInput expectedInput = expectedInputs[i]; + if (form.getDestinationLocation() != 0 && expectedInput.isRememberSiteValue()) + expectedInput.setDefaultSiteValue(getContainer(), form.getDestinationLocation(), inputs[i]); + if (i > 0) + comments.append("\n\n"); + comments.append(expectedInput.getTitle()).append(":\n"); + if (inputs[i] != null && inputs[i].length() > 0) + comments.append(inputs[i]); + else + comments.append("[Not provided]"); + } + + _specimenRequest = new SpecimenRequest(); + _specimenRequest.setComments(comments.toString()); + _specimenRequest.setContainer(getContainer()); + Timestamp ts = new Timestamp(System.currentTimeMillis()); + _specimenRequest.setCreated(ts); + _specimenRequest.setModified(ts); + _specimenRequest.setEntityId(GUID.makeGUID()); + Integer defaultSiteId = SpecimenService.get().getRequestCustomizer().getDefaultDestinationSiteId(); + // Default takes precedence if set + if (defaultSiteId != null) + { + _specimenRequest.setDestinationSiteId(defaultSiteId); + } + else if (form.getDestinationLocation() > 0) + { + _specimenRequest.setDestinationSiteId(form.getDestinationLocation()); + } + _specimenRequest.setStatusId(SpecimenRequestManager.get().getInitialRequestStatus(getContainer(), getUser(), false).getRowId()); + + DbScope scope = SpecimenSchema.get().getScope(); + try (DbScope.Transaction transaction = scope.ensureTransaction()) + { + if (!LocationManager.get().isSiteValidRequestingLocation(getContainer(), _specimenRequest.getDestinationSiteId())) + { + errors.reject(ERROR_MSG, "The requesting location is not valid."); + return false; + } + + User user = getUser(); + Container container = getContainer(); + _specimenRequest = SpecimenRequestManager.get().createRequest(getUser(), _specimenRequest, true); + List vials; + if (specimenIds != null && specimenIds.length > 0) + { + vials = new ArrayList<>(); + for (long specimenId : specimenIds) + { + Vial vial = SpecimenManagerNew.get().getVial(container, user, specimenId); + if (vial != null) + { + boolean isAvailable = vial.isAvailable(); + if (!isAvailable) + { + errors.reject(null, RequestabilityManager.makeSpecimenUnavailableMessage(vial, "This specimen has been removed from the list below.")); + } + else + vials.add(vial); + } + else + { + ExceptionUtil.logExceptionToMothership(getViewContext().getRequest(), + new IllegalStateException("Specimen ID " + specimenId + " was not found in container " + container.getId())); + } + } + if (errors.getErrorCount() == 0) + { + try + { + SpecimenRequestManager.get().createRequestSpecimenMapping(getUser(), _specimenRequest, vials, true, true); + } + catch (RequestabilityManager.InvalidRuleException e) + { + errors.reject(ERROR_MSG, "The request could not be created because a requestability rule is configured incorrectly. " + + "Please report this problem to an administrator. Error details: " + e.getMessage()); + return false; + } + catch (SpecimenRequestException e) + { + errors.reject(ERROR_MSG, "A vial that was available for request has become unavailable."); + return false; + } + } + else + { + long[] validSpecimenIds = new long[vials.size()]; + int index = 0; + for (Vial vial : vials) + validSpecimenIds[index++] = vial.getRowId(); + form.setSpecimenRowIds(validSpecimenIds); + return false; + } + } + transaction.commit(); + } + + if (!SettingsManager.get().isSpecimenShoppingCartEnabled(getContainer())) + { + try + { + StudyInternalService.get().sendNewRequestNotifications(SpecimenController2.this, _specimenRequest, errors); + } + catch (ConfigurationException | IOException e) + { + errors.reject(ERROR_MSG, e.getMessage()); + } + } + + if (errors.hasErrors()) + return false; + + Study study = getStudy(); + if (null == study) + throw new NotFoundException("No study exists in this folder."); + StudyInternalService.get().setLastSpecimenRequest(study, _specimenRequest.getRowId()); + return true; + } + + @Override + public ActionURL getSuccessURL(CreateSpecimenRequestForm createSpecimenRequestForm) + { + ActionURL modifiedReturnURL = null; + if (createSpecimenRequestForm.isExtendedRequestUrl()) + { + return getExtendedRequestURL(_specimenRequest.getRowId(), null); + } + if (createSpecimenRequestForm.getReturnUrl() != null) + { + modifiedReturnURL = createSpecimenRequestForm.getReturnActionURL(); + } + if (modifiedReturnURL != null && !createSpecimenRequestForm.isIgnoreReturnUrl()) + return modifiedReturnURL; + else + return getManageRequestURL(_specimenRequest.getRowId(), modifiedReturnURL != null ? modifiedReturnURL : null); + } + } + + private ModelAndView getCreateSpecimenRequestView(CreateSpecimenRequestForm form, BindException errors) throws SQLException + { + ensureSpecimenRequestsConfigured(true); + + RequestedSpecimens requested; + + try + { + requested = form.getSelectedSpecimens(getViewContext()); + } + catch (AmbiguousLocationException e) + { + // Even though this method (getCreateSpecimenRequestView) is used from multiple places, only HandleCreateSpecimenRequestAction + // receives a post; therefore, it's safe to say that the selectSpecimenProvider.jsp form should always post to + // HandleCreateSpecimenRequestAction. + return new JspView<>("/org/labkey/specimen/view/selectSpecimenProvider.jsp", + new SelectSpecimenProviderBean(form, e.getPossibleLocations(), new ActionURL(ShowCreateSpecimenRequestAction.class, getContainer())), errors); + } + + return new JspView<>("/org/labkey/specimen/view/requestSpecimens.jsp", + new NewRequestBean(getViewContext(), requested, form, errors)); + } + + @RequiresPermission(RequestSpecimensPermission.class) + public class ShowCreateSpecimenRequestAction extends SimpleViewAction + { + @Override + public ModelAndView getView(CreateSpecimenRequestForm form, BindException errors) throws Exception + { + return getCreateSpecimenRequestView(form, errors); + } + + @Override + public void addNavTrail(NavTree root) + { + setHelpTopic("specimenShopping"); + addSpecimenRequestsNavTrail(root); + root.addChild("New Specimen Request"); + } + } + + @RequiresPermission(RequestSpecimensPermission.class) + public class ShowAPICreateSpecimenRequestAction extends SimpleViewAction + { + @Override + public ModelAndView getView(CreateSpecimenRequestForm form, BindException errors) throws Exception + { + return getCreateSpecimenRequestView(form, errors); + } + + @Override + public void addNavTrail(NavTree root) + { + addSpecimenRequestsNavTrail(root); + root.addChild("New Specimen Request"); + } + } + + private ActionURL getManageRequestURL(int requestID, ActionURL returnUrl) + { + ActionURL url = urlProvider(SpecimenUrls.class).getManageRequestURL(getContainer()); + url.addParameter(IdForm.PARAMS.id, Integer.toString(requestID)); + if (returnUrl != null) + url.addReturnURL(returnUrl); + return url; + } + + private ActionURL getExtendedRequestURL(int requestID, String returnUrl) + { + ActionURL url = new ActionURL(ExtendedSpecimenRequestAction.class, getContainer()); + url.addParameter(IdForm.PARAMS.id, Integer.toString(requestID)); +// if (returnUrl != null) +// url.addReturnURL(returnUrl); + return url; + } + + @RequiresPermission(RequestSpecimensPermission.class) + public class ExtendedSpecimenRequestAction extends SimpleViewAction + { + @Override + public ModelAndView getView(CreateSpecimenRequestForm form, BindException errors) + { + VBox vbox = new VBox(); + + ExtendedSpecimenRequestView view = SpecimenManager.get().getExtendedSpecimenRequestView(getViewContext()); + if (view != null && view.isActive()) + { + HtmlView requestView = new HtmlView(view.getBody()); + vbox.addView(requestView); + } + else + { + vbox.addView(new HtmlView("An extended specimen request view has not been provided for this folder.")); + } + return vbox; + } + + @Override + public void addNavTrail(NavTree root) + { + addSpecimenRequestsNavTrail(root); + root.addChild("Extended Specimen Request"); + } + } } diff --git a/specimen/src/org/labkey/specimen/actions/SpecimenSearchBean.java b/specimen/src/org/labkey/specimen/actions/SpecimenSearchBean.java new file mode 100644 index 00000000..32497048 --- /dev/null +++ b/specimen/src/org/labkey/specimen/actions/SpecimenSearchBean.java @@ -0,0 +1,187 @@ +/* + * 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.specimen.actions; + +import org.jetbrains.annotations.Nullable; +import org.labkey.api.data.DisplayColumn; +import org.labkey.api.data.TableInfo; +import org.labkey.api.query.UserSchema; +import org.labkey.api.specimen.SpecimenQuerySchema; +import org.labkey.api.specimen.query.SpecimenQueryView; +import org.labkey.api.study.StudyService; +import org.labkey.api.util.DemoMode; +import org.labkey.api.view.ActionURL; +import org.labkey.api.view.ViewContext; + +import java.util.ArrayList; +import java.util.List; + +/** + * User: brittp + * Date: Oct 21, 2010 4:01:33 PM + */ +public class SpecimenSearchBean +{ + private boolean _detailsView; + private List _displayColumns; + private ActionURL _baseViewURL; + private String _dataRegionName; + private boolean _inWebPart; + private int _webPartId = 0; + private boolean _advancedExpanded; + + private static class DisplayColumnInfo + { + private final boolean _displayByDefault; + private final boolean _displayAsPickList; + private final boolean _forceDistinctQuery; + private final boolean _obfuscate; + private final @Nullable TableInfo _tableInfo; + + private String _orderBy; + + public DisplayColumnInfo(boolean displayByDefault, boolean displayAsPickList) + { + this(displayByDefault, displayAsPickList, false); + } + + public DisplayColumnInfo(boolean displayByDefault, boolean displayAsPickList, boolean forceDistinctQuery) + { + this(displayByDefault, displayAsPickList, forceDistinctQuery, false, null); + } + + public DisplayColumnInfo(boolean displayByDefault, boolean displayAsPickList, boolean forceDistinctQuery, boolean obfuscate, @Nullable TableInfo tableInfo) + { + _displayByDefault = displayByDefault; + _displayAsPickList = displayAsPickList; + _forceDistinctQuery = forceDistinctQuery; + _obfuscate = obfuscate; + _tableInfo = tableInfo; + } + + public boolean isDisplayAsPickList() + { + return _displayAsPickList; + } + + public boolean isDisplayByDefault() + { + return _displayByDefault; + } + + public boolean isForceDistinctQuery() + { + return _forceDistinctQuery; + } + + public boolean shouldObfuscate() + { + return _obfuscate; + } + + public String getOrderBy() + { + return _orderBy; + } + + public void setOrderBy(String orderBy) + { + _orderBy = orderBy; + } + + public @Nullable TableInfo getTableInfo() + { + return _tableInfo; + } + } + + public SpecimenSearchBean() + { + } + + public SpecimenSearchBean(ViewContext context, boolean detailsView, boolean inWebPart) + { + init(context, detailsView, inWebPart); + } + + public void init(ViewContext context, boolean detailsView, boolean inWebPart) + { + _inWebPart = inWebPart; + _detailsView = detailsView; + SpecimenQueryView view = SpecimenQueryView.createView(context, detailsView ? SpecimenQueryView.ViewType.VIALS : + SpecimenQueryView.ViewType.SUMMARY); + + _displayColumns = new ArrayList<>(); + for (DisplayColumn dc : view.getDisplayColumns()) + if (dc.getColumnInfo() != null) + _displayColumns.add(dc); + _dataRegionName = view.getDataRegionName(); + _baseViewURL = view.getBaseViewURL(); + + DisplayColumnInfo visitInfo = new DisplayColumnInfo(true, true); + visitInfo.setOrderBy("DisplayOrder"); + UserSchema schema = SpecimenQuerySchema.get(StudyService.get().getStudy(context.getContainer()), context.getUser()); + TableInfo simpleSpecimenTable = schema.getTable(SpecimenQuerySchema.SIMPLE_SPECIMEN_TABLE_NAME); + DisplayColumnInfo participantColInfo = new DisplayColumnInfo(true, true, true, DemoMode.isDemoMode(context), simpleSpecimenTable); + participantColInfo.setOrderBy("PTID"); + } + + public List getDisplayColumns() + { + return _displayColumns; + } + + public boolean isDetailsView() + { + return _detailsView; + } + + public ActionURL getBaseViewURL() + { + return _baseViewURL; + } + + public String getDataRegionName() + { + return _dataRegionName; + } + + public boolean isInWebPart() + { + return _inWebPart; + } + + public boolean isAdvancedExpanded() + { + return _advancedExpanded; + } + + public void setAdvancedExpanded(boolean advancedExpanded) + { + _advancedExpanded = advancedExpanded; + } + + public int getWebPartId() + { + return _webPartId; + } + + public void setWebPartId(int webPartId) + { + _webPartId = webPartId; + } +} diff --git a/specimen/src/org/labkey/specimen/actions/ViewRequestsHeaderBean.java b/specimen/src/org/labkey/specimen/actions/ViewRequestsHeaderBean.java new file mode 100644 index 00000000..815eb4fb --- /dev/null +++ b/specimen/src/org/labkey/specimen/actions/ViewRequestsHeaderBean.java @@ -0,0 +1,38 @@ +package org.labkey.specimen.actions; + +import org.labkey.api.specimen.SpecimenRequestManager; +import org.labkey.api.specimen.SpecimenRequestStatus; +import org.labkey.specimen.query.SpecimenRequestQueryView; +import org.labkey.api.view.ViewContext; + +import java.util.List; + +public class ViewRequestsHeaderBean +{ + public static final String PARAM_STATUSLABEL = "SpecimenRequest.Status/Label~eq"; + public static final String PARAM_CREATEDBY = "SpecimenRequest.CreatedBy/DisplayName~eq"; + + private final SpecimenRequestQueryView _view; + private final ViewContext _context; + + public ViewRequestsHeaderBean(ViewContext context, SpecimenRequestQueryView view) + { + _view = view; + _context = context; + } + + public SpecimenRequestQueryView getView() + { + return _view; + } + + public List getStauses() + { + return SpecimenRequestManager.get().getRequestStatuses(_context.getContainer(), _context.getUser()); + } + + public boolean isFilteredStatus(SpecimenRequestStatus status) + { + return status.getLabel().equals(_context.getActionURL().getParameter(PARAM_STATUSLABEL)); + } +} diff --git a/specimen/src/org/labkey/specimen/importer/SpecimenSettingsImporter.java b/specimen/src/org/labkey/specimen/importer/SpecimenSettingsImporter.java index fc81cac2..512c32bd 100644 --- a/specimen/src/org/labkey/specimen/importer/SpecimenSettingsImporter.java +++ b/specimen/src/org/labkey/specimen/importer/SpecimenSettingsImporter.java @@ -23,7 +23,7 @@ import org.labkey.api.specimen.SpecimenRequestManager; import org.labkey.api.specimen.SpecimenRequestManager.SpecimenRequestInput; import org.labkey.api.specimen.SpecimenRequestStatus; -import org.labkey.api.specimen.actions.ManageReqsBean; +import org.labkey.specimen.actions.ManageReqsBean; import org.labkey.api.specimen.importer.RequestabilityManager; import org.labkey.api.specimen.location.LocationCache; import org.labkey.api.specimen.location.LocationImpl; diff --git a/specimen/src/org/labkey/specimen/pipeline/SpecimenArchive.java b/specimen/src/org/labkey/specimen/pipeline/SpecimenArchive.java new file mode 100644 index 00000000..dc43d69d --- /dev/null +++ b/specimen/src/org/labkey/specimen/pipeline/SpecimenArchive.java @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2009-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.specimen.pipeline; + +import org.labkey.api.data.Container; +import org.labkey.api.study.SpecimenService; +import org.labkey.api.study.SpecimenTransform; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Date; +import java.util.Enumeration; +import java.util.List; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; + +/* +* User: adam +* Date: Sep 1, 2009 +* Time: 2:32:53 PM +*/ +public class SpecimenArchive +{ + private final File _definitionFile; + + public SpecimenArchive(File definitionFile) + { + _definitionFile = definitionFile; + } + + public File getDefinitionFile() + { + return _definitionFile; + } + + // Move to ZipUtil? + public List getEntryDescriptions(Container container) throws IOException + { + List entryList = new ArrayList<>(); + for (SpecimenTransform transform : SpecimenService.get().getSpecimenTransforms(container)) + { + if (transform.getFileType().isType(_definitionFile)) + { + entryList.add(new EntryDescription(_definitionFile.getName(), _definitionFile.length(), new Date(_definitionFile.lastModified()))); + return entryList; + } + } + + // standard non-transformed specimen archive + try (ZipFile zip = new ZipFile(_definitionFile)) + { + Enumeration entries = zip.entries(); + while (entries.hasMoreElements()) + { + ZipEntry entry = entries.nextElement(); + if (entry.isDirectory()) + continue; + entryList.add(new EntryDescription(entry.getName(), entry.getSize(), new Date(entry.getTime()))); + } + } + + return entryList; + } + + public static class EntryDescription + { + private final String _name; + private final long _size; + private final Date _date; + + public EntryDescription(String name, long size, Date date) + { + _name = name; + _size = size; + _date = date; + } + + public Date getDate() + { + return _date; + } + + public String getName() + { + return _name; + } + + public long getSize() + { + return _size; + } + } +} diff --git a/specimen/src/org/labkey/specimen/query/SpecimenEventQueryView.java b/specimen/src/org/labkey/specimen/query/SpecimenEventQueryView.java new file mode 100644 index 00000000..95d8cadb --- /dev/null +++ b/specimen/src/org/labkey/specimen/query/SpecimenEventQueryView.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2008-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.specimen.query; + +import org.labkey.api.data.SimpleFilter; +import org.labkey.api.data.Sort; +import org.labkey.api.query.FieldKey; +import org.labkey.api.query.QuerySettings; +import org.labkey.api.query.UserSchema; +import org.labkey.api.specimen.SpecimenQuerySchema; +import org.labkey.api.specimen.Vial; +import org.labkey.api.specimen.query.BaseSpecimenQueryView; +import org.labkey.api.study.Study; +import org.labkey.api.study.StudyService; +import org.labkey.api.view.ViewContext; + +/** + * User: brittp + * Date: Jan 26, 2007 + * Time: 10:13:50 AM + */ +public class SpecimenEventQueryView extends BaseSpecimenQueryView +{ + protected SpecimenEventQueryView(UserSchema schema, QuerySettings settings, SimpleFilter filter, Sort sort) + { + super(schema, settings, filter, sort); + } + + public static SpecimenEventQueryView createView(ViewContext context, Vial vial) + { + Study study = StudyService.get().getStudy(context.getContainer()); + UserSchema schema = SpecimenQuerySchema.get(study, context.getUser()); + String queryName = "SpecimenEvent"; + QuerySettings qs = schema.getSettings(context, queryName, queryName); + SimpleFilter filter = new SimpleFilter(FieldKey.fromParts("VialId"), vial.getRowId()); + Sort sort = new Sort("-ShipDate"); + return new SpecimenEventQueryView(schema, qs, filter, sort); + } +} diff --git a/specimen/src/org/labkey/specimen/query/SpecimenRequestQueryView.java b/specimen/src/org/labkey/specimen/query/SpecimenRequestQueryView.java new file mode 100644 index 00000000..484ec1c3 --- /dev/null +++ b/specimen/src/org/labkey/specimen/query/SpecimenRequestQueryView.java @@ -0,0 +1,222 @@ +/* + * Copyright (c) 2008-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.specimen.query; + +import org.jetbrains.annotations.Nullable; +import org.labkey.api.data.ColumnInfo; +import org.labkey.api.data.DataColumn; +import org.labkey.api.data.DataRegion; +import org.labkey.api.data.RenderContext; +import org.labkey.api.data.SimpleDisplayColumn; +import org.labkey.api.data.SimpleFilter; +import org.labkey.api.data.Sort; +import org.labkey.api.query.FieldKey; +import org.labkey.api.query.QuerySettings; +import org.labkey.api.query.UserSchema; +import org.labkey.api.security.User; +import org.labkey.api.specimen.SpecimenQuerySchema; +import org.labkey.api.specimen.SpecimenRequestManager; +import org.labkey.api.specimen.SpecimenRequestStatus; +import org.labkey.api.specimen.query.BaseSpecimenQueryView; +import org.labkey.api.specimen.security.permissions.ManageRequestsPermission; +import org.labkey.api.specimen.security.permissions.RequestSpecimensPermission; +import org.labkey.api.specimen.settings.SettingsManager; +import org.labkey.api.study.SpecimenUrls; +import org.labkey.api.study.Study; +import org.labkey.api.study.StudyService; +import org.labkey.api.study.StudyUtils; +import org.labkey.api.util.PageFlowUtil; +import org.labkey.api.view.ActionURL; +import org.labkey.api.view.NavTree; +import org.labkey.api.view.ViewContext; + +import java.io.IOException; +import java.io.Writer; +import java.util.Set; + +/** + * User: brittp + * Date: Apr 20, 2007 + * Time: 2:49:42 PM + */ +public class SpecimenRequestQueryView extends BaseSpecimenQueryView +{ + private NavTree[] _extraLinks; + private boolean _allowSortAndFilter = true; + private boolean _showOptionLinks = true; + + private static class RequestOptionDisplayColumn extends SimpleDisplayColumn + { + private final ColumnInfo _colCreatedBy; + private final ColumnInfo _colStatus; + private final boolean _showOptionLinks; + private final NavTree[] _extraLinks; + private final boolean _userIsSpecimenManager; + private final boolean _userCanRequest; + private final int _userId; + private final boolean _cartEnabled; + + private Integer _shoppingCartStatusRowId; + + public RequestOptionDisplayColumn(ViewContext context, boolean showOptionLinks, ColumnInfo colCreatedBy, ColumnInfo colStatus, NavTree... extraLinks) + { + _colStatus = colStatus; + _colCreatedBy = colCreatedBy; + User user = context.getUser(); + _userId = user.getUserId(); + _userIsSpecimenManager = context.getContainer().hasPermission(user, ManageRequestsPermission.class); + _userCanRequest = context.getContainer().hasPermission(user, RequestSpecimensPermission.class); + _showOptionLinks = showOptionLinks; + _extraLinks = extraLinks; + setTextAlign("right"); + setNoWrap(true); + setWidth("175em"); + _cartEnabled = SettingsManager.get().isSpecimenShoppingCartEnabled(context.getContainer()); + } + + @Override + public void addQueryColumns(Set set) + { + set.add(_colCreatedBy); + set.add(_colStatus); + } + + @Override + public void renderGridCellContents(RenderContext ctx, Writer out) throws IOException + { + StringBuilder content = new StringBuilder(); + // Use a div to set spacing around the buttons- otherwise they overlap the edges of their data grid cells: + content.append("

"); + if (_extraLinks != null) + { + for (NavTree link : _extraLinks) + content.append(PageFlowUtil.button(link.getText()).href(link.getHref())).append(" "); + } + + if (_showOptionLinks) + { + if (_cartEnabled) + { + if (_shoppingCartStatusRowId == null) + { + SpecimenRequestStatus cartStatus = SpecimenRequestManager.get().getRequestShoppingCartStatus(ctx.getContainer(), + ctx.getViewContext().getUser()); + _shoppingCartStatusRowId = cartStatus.getRowId(); + } + + Integer statusId = (Integer) _colStatus.getValue(ctx); + Integer ownerId = (Integer) _colCreatedBy.getValue(ctx); + boolean canEdit = _userIsSpecimenManager || (_userCanRequest && ownerId == _userId); + + if (statusId.intValue() == _shoppingCartStatusRowId.intValue() && canEdit) + { + ActionURL submitUrl = PageFlowUtil.urlProvider(SpecimenUrls.class).getSubmitRequestURL(ctx.getContainer(), "${requestId}"); + ActionURL cancelUrl = PageFlowUtil.urlProvider(SpecimenUrls.class).getDeleteRequestURL(ctx.getContainer(), "${requestId}"); + + content.append(PageFlowUtil.button("Submit").href(submitUrl).usePost(StudyUtils.SUBMISSION_WARNING)); + content.append(PageFlowUtil.button("Cancel").href(cancelUrl).usePost(StudyUtils.CANCELLATION_WARNING)); + } + } + + ActionURL detailsUrl = PageFlowUtil.urlProvider(SpecimenUrls.class).getRequestDetailsURL(ctx.getContainer(), "${requestId}"); + content.append(PageFlowUtil.button("Details").href(detailsUrl)); + } + content.append("
"); + setDisplayHtml(content.toString()); + super.renderGridCellContents(ctx, out); + } + } + + protected SpecimenRequestQueryView(UserSchema schema, QuerySettings settings, SimpleFilter filter, Sort sort, NavTree... extraLinks) + { + super(schema, settings, filter, sort); + _extraLinks = extraLinks; + } + + public static SpecimenRequestQueryView createView(ViewContext context) + { + return createView(context, null); + } + + public static SpecimenRequestQueryView createView(ViewContext context, @Nullable SimpleFilter filter) + { + Study study = StudyService.get().getStudy(context.getContainer()); + UserSchema schema = SpecimenQuerySchema.get(study, context.getUser()); + String queryName = "SpecimenRequest"; + QuerySettings qs = schema.getSettings(context, queryName, queryName); + + return new SpecimenRequestQueryView(schema, qs, addFilterClauses(filter), createDefaultSort()); + } + + private static Sort createDefaultSort() + { + return new Sort("-Created"); + } + + private static SimpleFilter addFilterClauses(@Nullable SimpleFilter filter) + { + if (filter == null) + filter = new SimpleFilter(); + filter.addCondition(FieldKey.fromParts("Hidden"), Boolean.FALSE); + return filter; + } + + @Override + protected DataRegion.ButtonBarPosition getButtonBarPosition() + { + return DataRegion.ButtonBarPosition.TOP; + } + + @Override + protected DataRegion createDataRegion() + { + DataRegion rgn = super.createDataRegion(); + DataColumn commentsDC = (DataColumn) rgn.getDisplayColumn("Comments"); + if (commentsDC != null) + { + commentsDC.setWidth("300px"); + commentsDC.setPreserveNewlines(true); + } + + if (_showOptionLinks || (_extraLinks != null && _extraLinks.length > 0)) + { + RequestOptionDisplayColumn optionCol = + new RequestOptionDisplayColumn(getViewContext(), _showOptionLinks, getTable().getColumn("CreatedBy"), + getTable().getColumn("Status"), _extraLinks); + rgn.addDisplayColumn(0, optionCol); + } + rgn.setSortable(_allowSortAndFilter); + rgn.setShowFilters(_allowSortAndFilter); + return rgn; + } + + public void setExtraLinks(boolean showOptionLinks, NavTree... extraLinks) + { + _showOptionLinks = showOptionLinks; + _extraLinks = extraLinks; + } + + public void setAllowSortAndFilter(boolean allowSortAndFilter) + { + _allowSortAndFilter = allowSortAndFilter; + } + + public void setShowCustomizeLink(boolean showCustomizeLink) + { + getSettings().setAllowCustomizeView(showCustomizeLink); + } +} diff --git a/specimen/src/org/labkey/specimen/security/permissions/LockSpecimensPermission.java b/specimen/src/org/labkey/specimen/security/permissions/LockSpecimensPermission.java new file mode 100644 index 00000000..e0ee6960 --- /dev/null +++ b/specimen/src/org/labkey/specimen/security/permissions/LockSpecimensPermission.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2009-2012 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.specimen.security.permissions; + +import org.labkey.api.security.permissions.AbstractPermission; +import org.labkey.api.study.StudyService; + +/* +* User: Dave +* Date: May 18, 2009 +* Time: 11:40:13 AM +*/ +public class LockSpecimensPermission extends AbstractPermission +{ + public LockSpecimensPermission() + { + super("Lock Specimens", + "Allows locking of specimens", + StudyService.get().getStudyModuleClass()); // TODO: Switch to SpecimenModule.class + } +} \ No newline at end of file diff --git a/specimen/src/org/labkey/specimen/security/permissions/ManageDisplaySettingsPermission.java b/specimen/src/org/labkey/specimen/security/permissions/ManageDisplaySettingsPermission.java new file mode 100644 index 00000000..5730b142 --- /dev/null +++ b/specimen/src/org/labkey/specimen/security/permissions/ManageDisplaySettingsPermission.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2009-2012 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.specimen.security.permissions; + +import org.labkey.api.security.permissions.AbstractPermission; +import org.labkey.api.study.StudyService; + +/* +* User: Dave +* Date: May 18, 2009 +* Time: 11:30:46 AM +*/ +public class ManageDisplaySettingsPermission extends AbstractPermission +{ + public ManageDisplaySettingsPermission() + { + super("Manage Specimen Display Settings", + "Allows management of display settings on the specimen request form", + StudyService.get().getStudyModuleClass()); // TODO: Switch to SpecimenModule.class + } +} \ No newline at end of file diff --git a/specimen/src/org/labkey/specimen/security/permissions/ManageNotificationsPermission.java b/specimen/src/org/labkey/specimen/security/permissions/ManageNotificationsPermission.java new file mode 100644 index 00000000..b6b9f66e --- /dev/null +++ b/specimen/src/org/labkey/specimen/security/permissions/ManageNotificationsPermission.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2009-2012 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.specimen.security.permissions; + +import org.labkey.api.security.permissions.AbstractPermission; +import org.labkey.api.study.StudyService; + +/* +* User: Dave +* Date: May 18, 2009 +* Time: 11:29:07 AM +*/ +public class ManageNotificationsPermission extends AbstractPermission +{ + public ManageNotificationsPermission() + { + super("Manage Notifications", + "Allows management of specimen request notifications", + StudyService.get().getStudyModuleClass()); // TODO: Switch to SpecimenModule.class + } +} \ No newline at end of file diff --git a/specimen/src/org/labkey/specimen/security/permissions/ManageRequestRequirementsPermission.java b/specimen/src/org/labkey/specimen/security/permissions/ManageRequestRequirementsPermission.java new file mode 100644 index 00000000..a8a33655 --- /dev/null +++ b/specimen/src/org/labkey/specimen/security/permissions/ManageRequestRequirementsPermission.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2009-2012 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.specimen.security.permissions; + +import org.labkey.api.security.permissions.AbstractPermission; +import org.labkey.api.study.StudyService; + +/* +* User: Dave +* Date: May 18, 2009 +* Time: 11:23:58 AM +*/ +public class ManageRequestRequirementsPermission extends AbstractPermission +{ + public ManageRequestRequirementsPermission() + { + super("Manage Specimen Request Default Requirements", + "Allows management of specimen request default requirements", + StudyService.get().getStudyModuleClass()); // TODO: Switch to SpecimenModule.class + } +} \ No newline at end of file diff --git a/specimen/src/org/labkey/specimen/security/roles/SpecimenCoordinatorRole.java b/specimen/src/org/labkey/specimen/security/roles/SpecimenCoordinatorRole.java index 214f31fc..f2bf9f3b 100644 --- a/specimen/src/org/labkey/specimen/security/roles/SpecimenCoordinatorRole.java +++ b/specimen/src/org/labkey/specimen/security/roles/SpecimenCoordinatorRole.java @@ -19,11 +19,7 @@ import org.labkey.api.security.SecurityManager; import org.labkey.api.security.permissions.EditSharedViewPermission; import org.labkey.api.specimen.security.permissions.EditSpecimenDataPermission; -import org.labkey.api.specimen.security.permissions.LockSpecimensPermission; -import org.labkey.api.specimen.security.permissions.ManageDisplaySettingsPermission; import org.labkey.api.specimen.security.permissions.ManageNewRequestFormPermission; -import org.labkey.api.specimen.security.permissions.ManageNotificationsPermission; -import org.labkey.api.specimen.security.permissions.ManageRequestRequirementsPermission; import org.labkey.api.specimen.security.permissions.ManageRequestSettingsPermission; import org.labkey.api.specimen.security.permissions.ManageRequestStatusesPermission; import org.labkey.api.specimen.security.permissions.ManageRequestsPermission; @@ -31,6 +27,10 @@ import org.labkey.api.specimen.security.permissions.RequestSpecimensPermission; import org.labkey.api.specimen.security.permissions.SetSpecimenCommentsPermission; import org.labkey.api.study.security.permissions.ManageStudyPermission; +import org.labkey.specimen.security.permissions.LockSpecimensPermission; +import org.labkey.specimen.security.permissions.ManageDisplaySettingsPermission; +import org.labkey.specimen.security.permissions.ManageNotificationsPermission; +import org.labkey.specimen.security.permissions.ManageRequestRequirementsPermission; /* * User: Dave diff --git a/specimen/src/org/labkey/specimen/view/SpecimenSearchWebPart.java b/specimen/src/org/labkey/specimen/view/SpecimenSearchWebPart.java new file mode 100644 index 00000000..f8899777 --- /dev/null +++ b/specimen/src/org/labkey/specimen/view/SpecimenSearchWebPart.java @@ -0,0 +1,39 @@ +/* + * 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.specimen.view; + +import org.labkey.specimen.actions.SpecimenSearchBean; +import org.labkey.api.view.JspView; + +/** + * User: brittp + * Date: Oct 21, 2010 1:39:16 PM + */ +public class SpecimenSearchWebPart extends JspView +{ + public SpecimenSearchWebPart() + { + this(true); + } + + public SpecimenSearchWebPart(boolean showVials) + { + super("/org/labkey/specimen/view/search.jsp", new SpecimenSearchBean()); + getModelBean().init(getViewContext(), showVials, true); + getModelBean().setWebPartId(getWebPartRowId()); + setTitle(showVials ? "Vial Search" : "Vial Group Search"); + } +} diff --git a/specimen/src/org/labkey/specimen/view/SpecimenSearchWebPartFactory.java b/specimen/src/org/labkey/specimen/view/SpecimenSearchWebPartFactory.java index 58c7c15a..5664e8a3 100644 --- a/specimen/src/org/labkey/specimen/view/SpecimenSearchWebPartFactory.java +++ b/specimen/src/org/labkey/specimen/view/SpecimenSearchWebPartFactory.java @@ -2,7 +2,6 @@ import org.jetbrains.annotations.NotNull; import org.labkey.api.security.permissions.ReadPermission; -import org.labkey.api.specimen.SpecimenSearchWebPart; import org.labkey.api.study.StudyService; import org.labkey.api.view.DefaultWebPartFactory; import org.labkey.api.view.HtmlView; diff --git a/specimen/src/org/labkey/specimen/view/SpecimenToolsWebPartFactory.java b/specimen/src/org/labkey/specimen/view/SpecimenToolsWebPartFactory.java index c4b8a7da..cbf45ce5 100644 --- a/specimen/src/org/labkey/specimen/view/SpecimenToolsWebPartFactory.java +++ b/specimen/src/org/labkey/specimen/view/SpecimenToolsWebPartFactory.java @@ -1,9 +1,7 @@ package org.labkey.specimen.view; -import org.labkey.specimen.actions.ShowSearchAction; import org.labkey.api.specimen.security.permissions.RequestSpecimensPermission; import org.labkey.api.specimen.settings.SettingsManager; -import org.labkey.api.study.SpecimenUrls; import org.labkey.api.study.StudyUrls; import org.labkey.api.study.security.permissions.ManageStudyPermission; import org.labkey.api.study.view.StudyToolsWebPart; @@ -12,7 +10,9 @@ import org.labkey.api.view.ActionURL; import org.labkey.api.view.ViewContext; import org.labkey.api.view.WebPartFactory; +import org.labkey.specimen.actions.ShowSearchAction; import org.labkey.specimen.actions.SpecimenController2.AutoReportListAction; +import org.labkey.specimen.actions.SpecimenController2.ShowCreateSpecimenRequestAction; import java.util.ArrayList; import java.util.List; @@ -41,7 +41,7 @@ protected List getItems(ViewContext portalCtx) if (SettingsManager.get().isSpecimenRequestEnabled(portalCtx.getContainer())) { if (portalCtx.getContainer().hasPermission(portalCtx.getUser(), RequestSpecimensPermission.class)) - items.add(new StudyToolsWebPart.Item("New Request", iconBase + "specimen_request.png", PageFlowUtil.urlProvider(SpecimenUrls.class).getShowCreateSpecimenRequestURL(portalCtx.getContainer()))); + items.add(new StudyToolsWebPart.Item("New Request", iconBase + "specimen_request.png", new ActionURL(ShowCreateSpecimenRequestAction.class, portalCtx.getContainer()))); } items.add(new StudyToolsWebPart.Item("Specimen Reports", iconBase + "specimen_report.png", new ActionURL(AutoReportListAction.class, portalCtx.getContainer()))); diff --git a/specimen/src/org/labkey/specimen/view/importSpecimens.jsp b/specimen/src/org/labkey/specimen/view/importSpecimens.jsp index 15f0bd44..da369aed 100644 --- a/specimen/src/org/labkey/specimen/view/importSpecimens.jsp +++ b/specimen/src/org/labkey/specimen/view/importSpecimens.jsp @@ -16,11 +16,11 @@ */ %> <%@ taglib prefix="labkey" uri="http://www.labkey.org/taglib" %> -<%@ page import="org.labkey.api.specimen.pipeline.SpecimenArchive"%> <%@ page import="org.labkey.api.view.HttpView"%> <%@ page import="org.labkey.api.view.JspView"%> -<%@ page import="org.labkey.specimen.actions.SpecimenController2.ImportSpecimensBean" %> +<%@ page import="org.labkey.specimen.actions.SpecimenController2.ImportSpecimensBean"%> <%@ page import="org.labkey.specimen.actions.SpecimenController2.SubmitSpecimenBatchImportAction" %> +<%@ page import="org.labkey.specimen.pipeline.SpecimenArchive" %> <%@ page import="java.util.List" %> <%@ page import="java.util.zip.ZipException" %> <%@ page extends="org.labkey.api.jsp.JspBase" %> diff --git a/specimen/src/org/labkey/specimen/view/manageDefaultReqs.jsp b/specimen/src/org/labkey/specimen/view/manageDefaultReqs.jsp index 0fe82ccc..aa36e0a3 100644 --- a/specimen/src/org/labkey/specimen/view/manageDefaultReqs.jsp +++ b/specimen/src/org/labkey/specimen/view/manageDefaultReqs.jsp @@ -15,14 +15,14 @@ * limitations under the License. */ %> -<%@ page import="org.labkey.api.specimen.actions.ManageReqsBean" %> <%@ page import="org.labkey.api.specimen.model.SpecimenRequestActor" %> <%@ page import="org.labkey.api.specimen.requirements.SpecimenRequestRequirement" %> -<%@ page import="org.labkey.api.study.StudyUrls"%> +<%@ 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.ActionURL"%> <%@ page import="org.labkey.api.view.HttpView" %> <%@ page import="org.labkey.api.view.JspView" %> +<%@ page import="org.labkey.specimen.actions.ManageReqsBean" %> <%@ page import="org.labkey.specimen.actions.SpecimenController2" %> <%@ page import="org.labkey.specimen.actions.SpecimenController2.ManageDefaultReqsAction" %> <%@ taglib prefix="labkey" uri="http://www.labkey.org/taglib" %> diff --git a/specimen/src/org/labkey/specimen/view/requestSpecimens.jsp b/specimen/src/org/labkey/specimen/view/requestSpecimens.jsp new file mode 100644 index 00000000..0efb413a --- /dev/null +++ b/specimen/src/org/labkey/specimen/view/requestSpecimens.jsp @@ -0,0 +1,244 @@ +<% +/* + * 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.specimen.SpecimenManager"%> +<%@ page import="org.labkey.api.specimen.SpecimenMigrationService"%> +<%@ page import="org.labkey.api.specimen.SpecimenRequestManager.SpecimenRequestInput"%> +<%@ page import="org.labkey.api.specimen.Vial"%> +<%@ page import="org.labkey.api.specimen.location.LocationImpl"%> +<%@ page import="org.labkey.api.specimen.location.LocationManager"%> +<%@ page import="org.labkey.api.specimen.settings.SettingsManager"%> +<%@ page import="org.labkey.api.study.SpecimenService"%> +<%@ page import="org.labkey.api.view.HttpView" %> +<%@ page import="org.labkey.api.view.JspView" %> +<%@ page import="org.labkey.api.view.ViewContext" %> +<%@ page import="org.labkey.api.view.template.ClientDependencies" %> +<%@ page import="org.labkey.specimen.actions.SpecimenController2.CreateSpecimenRequestForm" %> +<%@ page import="org.labkey.specimen.actions.SpecimenController2.HandleCreateSpecimenRequestAction" %> +<%@ page import="org.labkey.specimen.actions.SpecimenController2.NewRequestBean" %> +<%@ page import="org.springframework.validation.BindException" %> +<%@ page import="org.springframework.validation.ObjectError" %> +<%@ page import="java.util.List" %> +<%@ page import="java.util.Map" %> +<%@ taglib prefix="labkey" uri="http://www.labkey.org/taglib" %> +<%@ page extends="org.labkey.api.jsp.JspBase" %> +<%! + @Override + public void addClientDependencies(ClientDependencies dependencies) + { + dependencies.add("Ext4"); + } +%> +<% + JspView me = (JspView) HttpView.currentView(); + NewRequestBean bean = me.getModelBean(); + ViewContext context = getViewContext(); + Container c = getContainer(); + List locations = LocationManager.get().getValidRequestingLocations(c); + boolean shoppingCart = SettingsManager.get().isSpecimenShoppingCartEnabled(c); + boolean hasExtendedRequestView = SpecimenManager.get().getExtendedSpecimenRequestView(context) != null; + List vials = bean.getVials(); + SpecimenRequestInput[] inputs = bean.getInputs(); +%> + + <% + BindException errors = bean.getErrors(); + if (errors != null) + { + for (ObjectError e : errors.getAllErrors()) + { + %><%=h(context.getMessage(e))%>
<% + } + } + %> +
+ + + + <%=generateReturnUrlFormField(bean.getReturnUrl())%> + <% + if (vials != null) + { + for (Vial vial : vials) + { + %> + + <% + } + } + %> + + <% + if (shoppingCart) + { + %> + + + + <% + } + if (SpecimenService.get().getRequestCustomizer().getDefaultDestinationSiteId() != null) + { %> + + <% } + else + { %> + + + + + + + <% } %> + <% + for (int i = 0; i < inputs.length; i++) + { + SpecimenRequestInput input = inputs[i]; + %> + + + + + + + + + + <% + } + %> + + + + + + <% + if (vials != null && vials.size() > 0) + { + %> + + + + + + + <% + } + %> + +
Please fill out this form to create a new specimen request. You will have the chance to add or remove specimens before the request is submitted.
Requesting Location (Required):
+ + +
+ +
<%= h(input.getTitle()) %> <%= text(input.isRequired() ? "(Required)" : "(Optional)") %>: +
<%= h(input.getHelpText()) %>
+ <% + if (input.isMultiLine()) + { + %> + + <% + } + else + { + %> + value="<%= h(bean.getValue(i)) %>"> + <% + } + %> +
+ + + <% + boolean hasReturnURL = bean.getReturnUrl() != null; + if (hasExtendedRequestView) + { + %> + <%= button("Save & Continue").submit(true).onClick("document.CreateSpecimenRequest." + CreateSpecimenRequestForm.PARAMS.extendedRequestUrl.name() + ".value='true'; return true;") %> + <% + } + else + { + if (hasReturnURL) + { + %> + <%= button((shoppingCart ? "Create" : "Submit") + " and Return to Specimens").submit(true) %> + <% + } + %> + <%= button((shoppingCart ? "Create" : "Submit") + " and View Details").submit(true) + .onClick("document.CreateSpecimenRequest." + CreateSpecimenRequestForm.PARAMS.ignoreReturnUrl.name() + ".value='true'; return true;") %> + <% + } + %> + <%= text(hasReturnURL ? button("Cancel").href(bean.getReturnUrl()).toString() : button("Cancel").href(SpecimenMigrationService.get().getViewRequestsURL(getContainer())).toString()) %> +
Selected Specimens:
<% me.include(bean.getSpecimenQueryView(), out); %>
+
\ No newline at end of file diff --git a/specimen/src/org/labkey/specimen/view/search.jsp b/specimen/src/org/labkey/specimen/view/search.jsp index 5d0b6b0e..487bd9b7 100644 --- a/specimen/src/org/labkey/specimen/view/search.jsp +++ b/specimen/src/org/labkey/specimen/view/search.jsp @@ -19,7 +19,7 @@ <%@ page import="org.labkey.api.view.JspView" %> <%@ page import="org.labkey.api.view.template.ClientDependencies" %> <%@ page import="org.labkey.api.view.template.ClientDependency" %> -<%@ page import="org.labkey.api.specimen.SpecimenSearchBean" %> +<%@ page import="org.labkey.specimen.actions.SpecimenSearchBean" %> <%@ page extends="org.labkey.api.jsp.JspBase" %> <%! @Override diff --git a/specimen/src/org/labkey/specimen/view/viewRequestsHeader.jsp b/specimen/src/org/labkey/specimen/view/viewRequestsHeader.jsp index d62332a2..0f92389f 100644 --- a/specimen/src/org/labkey/specimen/view/viewRequestsHeader.jsp +++ b/specimen/src/org/labkey/specimen/view/viewRequestsHeader.jsp @@ -16,11 +16,11 @@ */ %> <%@ page import="org.labkey.api.specimen.SpecimenRequestStatus" %> -<%@ page import="org.labkey.api.specimen.actions.ViewRequestsHeaderBean" %> <%@ page import="org.labkey.api.view.ActionURL" %> <%@ page import="org.labkey.api.view.HttpView" %> <%@ page import="org.labkey.api.view.JspView" %> <%@ page import="org.labkey.api.view.ViewContext" %> +<%@ page import="org.labkey.specimen.actions.ViewRequestsHeaderBean" %> <%@ page extends="org.labkey.api.jsp.JspBase" %> <% JspView me = (JspView) HttpView.currentView(); diff --git a/specimen/src/org/labkey/specimen/writer/SpecimenSettingsWriter.java b/specimen/src/org/labkey/specimen/writer/SpecimenSettingsWriter.java index c5b1e209..e9ad5c6c 100644 --- a/specimen/src/org/labkey/specimen/writer/SpecimenSettingsWriter.java +++ b/specimen/src/org/labkey/specimen/writer/SpecimenSettingsWriter.java @@ -20,7 +20,7 @@ import org.labkey.api.specimen.SpecimenRequestManager; import org.labkey.api.specimen.SpecimenRequestManager.SpecimenRequestInput; import org.labkey.api.specimen.SpecimenRequestStatus; -import org.labkey.api.specimen.actions.ManageReqsBean; +import org.labkey.specimen.actions.ManageReqsBean; import org.labkey.api.specimen.importer.RequestabilityManager; import org.labkey.api.specimen.model.SpecimenRequestActor; import org.labkey.api.specimen.requirements.SpecimenRequestRequirement; From 84264764f82ea8c9df9621c9c33779d51d0b4786 Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Fri, 4 Jun 2021 14:24:30 -0700 Subject: [PATCH 337/494] Issue 43290: Need to alias location of SpecimenRequester and SpecimenCoordinator roles Original PR: https://github.com/LabKey/platform/pull/2323 --- .../security/roles/SpecimenCoordinatorRole.java | 10 ++++++++++ .../specimen/security/roles/SpecimenRequesterRole.java | 10 ++++++++++ 2 files changed, 20 insertions(+) diff --git a/specimen/src/org/labkey/specimen/security/roles/SpecimenCoordinatorRole.java b/specimen/src/org/labkey/specimen/security/roles/SpecimenCoordinatorRole.java index 214f31fc..f389b675 100644 --- a/specimen/src/org/labkey/specimen/security/roles/SpecimenCoordinatorRole.java +++ b/specimen/src/org/labkey/specimen/security/roles/SpecimenCoordinatorRole.java @@ -15,6 +15,7 @@ */ package org.labkey.specimen.security.roles; +import org.jetbrains.annotations.NotNull; import org.labkey.api.security.Group; import org.labkey.api.security.SecurityManager; import org.labkey.api.security.permissions.EditSharedViewPermission; @@ -32,6 +33,9 @@ import org.labkey.api.specimen.security.permissions.SetSpecimenCommentsPermission; import org.labkey.api.study.security.permissions.ManageStudyPermission; +import java.util.Collection; +import java.util.Set; + /* * User: Dave * Date: May 13, 2009 @@ -62,4 +66,10 @@ public SpecimenCoordinatorRole() addExcludedPrincipal(SecurityManager.getGroup(Group.groupGuests)); addExcludedPrincipal(SecurityManager.getGroup(Group.groupUsers)); } + + @Override + public @NotNull Collection getSerializationAliases() + { + return Set.of("org.labkey.study.security.roles.SpecimenCoordinatorRole"); + } } \ No newline at end of file diff --git a/specimen/src/org/labkey/specimen/security/roles/SpecimenRequesterRole.java b/specimen/src/org/labkey/specimen/security/roles/SpecimenRequesterRole.java index 6c37fceb..58c35f1f 100644 --- a/specimen/src/org/labkey/specimen/security/roles/SpecimenRequesterRole.java +++ b/specimen/src/org/labkey/specimen/security/roles/SpecimenRequesterRole.java @@ -15,10 +15,14 @@ */ package org.labkey.specimen.security.roles; +import org.jetbrains.annotations.NotNull; import org.labkey.api.security.Group; import org.labkey.api.security.SecurityManager; import org.labkey.api.specimen.security.permissions.RequestSpecimensPermission; +import java.util.Collection; +import java.util.Set; + /* * User: Dave * Date: May 18, 2009 @@ -33,4 +37,10 @@ public SpecimenRequesterRole() RequestSpecimensPermission.class); addExcludedPrincipal(SecurityManager.getGroup(Group.groupGuests)); } + + @Override + public @NotNull Collection getSerializationAliases() + { + return Set.of("org.labkey.study.security.roles.SpecimenRequesterRole"); + } } \ No newline at end of file From 0d4a5c08027da0f9293f9fb8f05b70f7816df5c7 Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Tue, 15 Jun 2021 12:44:36 -0700 Subject: [PATCH 338/494] Move more specimen code Original PR: https://github.com/LabKey/platform/pull/2326 --- .../SpecimenCommentAuditProvider.java | 6 +- .../org/labkey/specimen/SpecimenModule.java | 105 +- .../labkey/specimen/SpecimenServiceImpl.java | 4 +- .../actions/HiddenFormInputGenerator.java | 8 + .../org/labkey/specimen/actions/IdForm.java | 32 + .../org/labkey/specimen/actions/IdTypes.java | 8 + .../specimen/actions/ManageRequestBean.java | 258 + .../actions/ManageRequestInputsBean.java | 36 + .../actions/SelectSpecimenProviderBean.java | 33 + .../actions/ShowGroupMembersAction.java | 25 +- .../specimen/actions/ShowSearchAction.java | 12 +- .../actions/ShowUploadSpecimensAction.java | 7 +- .../actions/SpecimenApiController.java | 1 - .../specimen/actions/SpecimenController.java | 5622 +++++++++++++++++ .../specimen/actions/SpecimenController2.java | 2092 ------ .../specimen/actions/SpecimenEventBean.java | 25 + .../specimen/actions/SpecimenHeaderBean.java | 143 + .../actions/SpecimenViewTypeForm.java | 42 + .../specimen/actions/SpecimensViewBean.java | 37 + .../actions/UpdateSpecimenCommentsBean.java | 157 + .../ActorNotificationRecipientSet.java | 126 + .../specimen/pipeline/SpecimenPipeline.java | 2 +- .../query/SpecimenRequestQueryView.java | 11 +- .../report/SpecimenTypeVisitReport.java | 4 +- .../report/SpecimenVisitReportAction.java | 6 +- .../report/SpecimenVisitReportParameters.java | 2 +- .../participant/ParticipantVisitReport.java | 5 +- .../request/RequestParticipantReport.java | 5 +- .../permissions/LockSpecimensPermission.java | 4 +- .../ManageDisplaySettingsPermission.java | 4 +- .../ManageNewRequestFormPermission.java | 34 + .../ManageNotificationsPermission.java | 4 +- .../ManageRequestRequirementsPermission.java | 4 +- .../ManageRequestStatusesPermission.java | 33 + .../ManageSpecimenActorsPermission.java | 33 + .../SetSpecimenCommentsPermission.java | 34 + .../roles/SpecimenCoordinatorRole.java | 8 +- .../specimen/view/ManageSpecimenView.java | 11 + .../view/SpecimenToolsWebPartFactory.java | 7 +- .../labkey/specimen/view/SpecimenWebPart.java | 64 + .../specimen/view/SpecimenWebPartFactory.java | 1 - .../labkey/specimen/view/chooseImporter.jsp | 161 + .../view/configRequestabilityRules.jsp | 2 +- .../labkey/specimen/view/importSpecimens.jsp | 4 +- .../labkey/specimen/view/labSpecimenLists.jsp | 198 + .../labkey/specimen/view/manageActorOrder.jsp | 104 + .../org/labkey/specimen/view/manageActors.jsp | 143 + .../labkey/specimen/view/manageComments.jsp | 215 + .../specimen/view/manageDefaultReqs.jsp | 6 +- .../labkey/specimen/view/manageDisplay.jsp | 2 +- .../specimen/view/manageLocationTypes.jsp | 113 + .../specimen/view/manageNotifications.jsp | 2 +- .../view/manageRepositorySettings.jsp | 2 +- .../labkey/specimen/view/manageRequest.jsp | 544 ++ .../specimen/view/manageRequestInputs.jsp | 201 + .../specimen/view/manageRequestStatus.jsp | 120 + .../specimen/view/manageRequirement.jsp | 200 + .../specimen/view/manageSpecimenWebPart.jsp | 4 +- .../labkey/specimen/view/manageSpecimens.jsp | 208 + .../specimen/view/manageStatusOrder.jsp | 115 + .../labkey/specimen/view/manageStatuses.jsp | 146 + .../labkey/specimen/view/requestSpecimens.jsp | 11 +- .../specimen/view/selectSpecimenProvider.jsp | 4 +- .../src/org/labkey/specimen/view/specimen.jsp | 106 + .../labkey/specimen/view/specimenHeader.jsp | 144 + .../labkey/specimen/view/updateComments.jsp | 258 + .../specimen/view/uploadSimpleSpecimens.jsp | 2 +- .../src/org/labkey/specimen/view/webPart.jsp | 366 ++ 68 files changed, 10202 insertions(+), 2234 deletions(-) create mode 100644 specimen/src/org/labkey/specimen/actions/HiddenFormInputGenerator.java create mode 100644 specimen/src/org/labkey/specimen/actions/IdForm.java create mode 100644 specimen/src/org/labkey/specimen/actions/IdTypes.java create mode 100644 specimen/src/org/labkey/specimen/actions/ManageRequestBean.java create mode 100644 specimen/src/org/labkey/specimen/actions/ManageRequestInputsBean.java create mode 100644 specimen/src/org/labkey/specimen/actions/SelectSpecimenProviderBean.java create mode 100644 specimen/src/org/labkey/specimen/actions/SpecimenController.java delete mode 100644 specimen/src/org/labkey/specimen/actions/SpecimenController2.java create mode 100644 specimen/src/org/labkey/specimen/actions/SpecimenEventBean.java create mode 100644 specimen/src/org/labkey/specimen/actions/SpecimenHeaderBean.java create mode 100644 specimen/src/org/labkey/specimen/actions/SpecimenViewTypeForm.java create mode 100644 specimen/src/org/labkey/specimen/actions/SpecimensViewBean.java create mode 100644 specimen/src/org/labkey/specimen/actions/UpdateSpecimenCommentsBean.java create mode 100644 specimen/src/org/labkey/specimen/notifications/ActorNotificationRecipientSet.java create mode 100644 specimen/src/org/labkey/specimen/security/permissions/ManageNewRequestFormPermission.java create mode 100644 specimen/src/org/labkey/specimen/security/permissions/ManageRequestStatusesPermission.java create mode 100644 specimen/src/org/labkey/specimen/security/permissions/ManageSpecimenActorsPermission.java create mode 100644 specimen/src/org/labkey/specimen/security/permissions/SetSpecimenCommentsPermission.java create mode 100644 specimen/src/org/labkey/specimen/view/ManageSpecimenView.java create mode 100644 specimen/src/org/labkey/specimen/view/SpecimenWebPart.java create mode 100644 specimen/src/org/labkey/specimen/view/chooseImporter.jsp create mode 100644 specimen/src/org/labkey/specimen/view/labSpecimenLists.jsp create mode 100644 specimen/src/org/labkey/specimen/view/manageActorOrder.jsp create mode 100644 specimen/src/org/labkey/specimen/view/manageActors.jsp create mode 100644 specimen/src/org/labkey/specimen/view/manageComments.jsp create mode 100644 specimen/src/org/labkey/specimen/view/manageLocationTypes.jsp create mode 100644 specimen/src/org/labkey/specimen/view/manageRequest.jsp create mode 100644 specimen/src/org/labkey/specimen/view/manageRequestInputs.jsp create mode 100644 specimen/src/org/labkey/specimen/view/manageRequestStatus.jsp create mode 100644 specimen/src/org/labkey/specimen/view/manageRequirement.jsp create mode 100644 specimen/src/org/labkey/specimen/view/manageSpecimens.jsp create mode 100644 specimen/src/org/labkey/specimen/view/manageStatusOrder.jsp create mode 100644 specimen/src/org/labkey/specimen/view/manageStatuses.jsp create mode 100644 specimen/src/org/labkey/specimen/view/specimen.jsp create mode 100644 specimen/src/org/labkey/specimen/view/specimenHeader.jsp create mode 100644 specimen/src/org/labkey/specimen/view/updateComments.jsp create mode 100644 specimen/src/org/labkey/specimen/view/webPart.jsp diff --git a/specimen/src/org/labkey/specimen/SpecimenCommentAuditProvider.java b/specimen/src/org/labkey/specimen/SpecimenCommentAuditProvider.java index cd3f0c68..52cf835e 100644 --- a/specimen/src/org/labkey/specimen/SpecimenCommentAuditProvider.java +++ b/specimen/src/org/labkey/specimen/SpecimenCommentAuditProvider.java @@ -18,7 +18,6 @@ import org.labkey.api.audit.AbstractAuditTypeProvider; import org.labkey.api.audit.AuditTypeEvent; import org.labkey.api.audit.AuditTypeProvider; -import org.labkey.api.audit.query.AbstractAuditDomainKind; import org.labkey.api.audit.query.DefaultAuditTypeTable; import org.labkey.api.data.ColumnInfo; import org.labkey.api.data.Container; @@ -34,10 +33,9 @@ import org.labkey.api.query.UserSchema; import org.labkey.api.specimen.SpecimenCommentAuditDomainKind; import org.labkey.api.specimen.SpecimenCommentAuditEvent; -import org.labkey.api.study.SpecimenUrls; import org.labkey.api.util.Link; -import org.labkey.api.util.PageFlowUtil; import org.labkey.api.view.ActionURL; +import org.labkey.specimen.actions.SpecimenController; import java.io.IOException; import java.io.Writer; @@ -143,7 +141,7 @@ public void renderGridCellContents(RenderContext ctx, Writer out) throws IOExcep return; } - ActionURL url = PageFlowUtil.urlProvider(SpecimenUrls.class).getCommentURL(container, globalUniqueId); + ActionURL url = SpecimenController.getCommentURL(container, globalUniqueId); out.write(new Link.LinkBuilder(globalUniqueId).href(url).clearClasses().toString()); } }; diff --git a/specimen/src/org/labkey/specimen/SpecimenModule.java b/specimen/src/org/labkey/specimen/SpecimenModule.java index e66da734..0faf3837 100644 --- a/specimen/src/org/labkey/specimen/SpecimenModule.java +++ b/specimen/src/org/labkey/specimen/SpecimenModule.java @@ -34,6 +34,7 @@ import org.labkey.api.specimen.model.SpecimenRequestEventType; import org.labkey.api.specimen.view.SpecimenRequestNotificationEmailTemplate; import org.labkey.api.study.SpecimenService; +import org.labkey.api.study.StudyInternalService; import org.labkey.api.study.StudyService; import org.labkey.api.study.importer.SimpleStudyImporterRegistry; import org.labkey.api.study.writer.SimpleStudyWriterRegistry; @@ -41,12 +42,9 @@ import org.labkey.api.view.ActionURL; import org.labkey.api.view.HttpView; import org.labkey.api.view.WebPartFactory; -import org.labkey.specimen.actions.ShowGroupMembersAction; -import org.labkey.specimen.actions.ShowSearchAction; import org.labkey.specimen.actions.ShowUploadSpecimensAction; import org.labkey.specimen.actions.SpecimenApiController; -import org.labkey.specimen.actions.SpecimenController2; -import org.labkey.specimen.actions.SpecimenController2.OverviewAction; +import org.labkey.specimen.actions.SpecimenController; import org.labkey.specimen.importer.DefaultSpecimenImportStrategyFactory; import org.labkey.specimen.importer.SpecimenSchemaImporter; import org.labkey.specimen.importer.SpecimenSettingsImporter; @@ -55,6 +53,7 @@ import org.labkey.specimen.pipeline.SpecimenPipeline; import org.labkey.specimen.security.roles.SpecimenCoordinatorRole; import org.labkey.specimen.security.roles.SpecimenRequesterRole; +import org.labkey.specimen.view.ManageSpecimenView; import org.labkey.specimen.view.SpecimenReportWebPartFactory; import org.labkey.specimen.view.SpecimenSearchWebPartFactory; import org.labkey.specimen.view.SpecimenToolsWebPartFactory; @@ -84,10 +83,10 @@ public String getName() protected Collection createWebPartFactories() { return List.of( + new SpecimenReportWebPartFactory(), new SpecimenSearchWebPartFactory(HttpView.BODY), new SpecimenToolsWebPartFactory(), - new SpecimenWebPartFactory(), - new SpecimenReportWebPartFactory() + new SpecimenWebPartFactory() ); } @@ -112,7 +111,7 @@ protected void init() AttachmentService.get().registerAttachmentType(SpecimenRequestEventType.get()); - addController("specimen2", SpecimenController2.class); + addController("specimen", SpecimenController.class, "study-samples"); addController("specimen-api", SpecimenApiController.class, "study-samples-api", "specimens-api"); // Register early -- some modules don't declare a runtime dependency on specimen module, but will use the @@ -123,112 +122,81 @@ protected void init() SpecimenMigrationService.setInstance(new SpecimenMigrationService() { @Override - public ActionURL getSpecimenRequestEventDownloadURL(SpecimenRequestEvent event, String name) - { - return SpecimenController2.getDownloadURL(event, name); - } - - @Override - public ActionURL getOverviewURL(Container c) - { - return new ActionURL(OverviewAction.class, c); - } - - @Override - public ActionURL getShowGroupMembersURL(Container c, int rowId, @Nullable Integer locationId, @Nullable ActionURL returnUrl) + public ActionURL getBeginURL(Container c) { - ActionURL url = new ActionURL(ShowGroupMembersAction.class, c); - url.addParameter("id", Integer.toString(rowId)); - if (locationId != null) - url.addParameter("locationId", locationId); - if (returnUrl != null) - url.addReturnURL(returnUrl); - - return url; + return new ActionURL(SpecimenController.BeginAction.class, c); } @Override - public ActionURL getUploadSpecimensURL(Container c) + public ActionURL getManageRequestStatusURL(Container c, int requestId) { - return new ActionURL(ShowUploadSpecimensAction.class, c); + return SpecimenController.getManageRequestStatusURL(c, requestId); } @Override - public ActionURL getAutoReportListURL(Container c) + public ActionURL getManageRequestURL(Container c, int requestId, @Nullable ActionURL returnUrl) { - return new ActionURL(SpecimenController2.AutoReportListAction.class, c); + return SpecimenController.getManageRequestURL(c, requestId, returnUrl); } @Override - public ActionURL getShowSearchURL(Container c, boolean showVials) + public ActionURL getSelectedSpecimensURL(Container c) { - return new ActionURL(ShowSearchAction.class, c).addParameter("showVials", showVials); + return new ActionURL(SpecimenController.SelectedSpecimensAction.class, c); } @Override - public ActionURL getSpecimenRequestConfigRequiredURL(Container c) + public ActionURL getSpecimenRequestEventDownloadURL(SpecimenRequestEvent event, String name) { - return new ActionURL(SpecimenController2.SpecimenRequestConfigRequiredAction.class, c); + return SpecimenController.getDownloadURL(event, name); } @Override - public ActionURL getConfigureRequestabilityRulesURL(Container c) + public ActionURL getSpecimensURL(Container c) { - return new ActionURL(SpecimenController2.ConfigureRequestabilityRulesAction.class, c); + return SpecimenController.getSpecimensURL(c); } @Override - public ActionURL getViewRequestsURL(Container c) + public ActionURL getSpecimensURL(Container c, boolean showVials) { - return new ActionURL(SpecimenController2.ViewRequestsAction.class, c); + return SpecimenController.getSpecimensURL(c, showVials); } @Override public ActionURL getSpecimenEventsURL(Container c, ActionURL returnURL) { - return new ActionURL(SpecimenController2.SpecimenEventsAction.class, c).addReturnURL(returnURL); - } - - @Override - public ActionURL getManageNotificationsURL(Container c) - { - return new ActionURL(SpecimenController2.ManageNotificationsAction.class, c); + return new ActionURL(SpecimenController.SpecimenEventsAction.class, c).addReturnURL(returnURL); } @Override - public ActionURL getManageDisplaySettings(Container c) + public ActionURL getUploadSpecimensURL(Container c) { - return new ActionURL(SpecimenController2.ManageDisplaySettingsAction.class, c); + return new ActionURL(ShowUploadSpecimensAction.class, c); } @Override - public ActionURL getManageRepositorySettingsURL(Container c) + public ActionURL getViewRequestsURL(Container c) { - return new ActionURL(SpecimenController2.ManageRepositorySettingsAction.class, c); + return new ActionURL(SpecimenController.ViewRequestsAction.class, c); } @Override - public ActionURL getManageDefaultReqsSettingsURL(Container c) + public Class getClearCommentsActionClass() { - return new ActionURL(SpecimenController2.ManageDefaultReqsAction.class, c); + return SpecimenController.ClearCommentsAction.class; } @Override public Class getShowCreateSpecimenRequestActionClass() { - return SpecimenController2.ShowCreateSpecimenRequestAction.class; - } - - @Override - public Class getShowAPICreateSpecimenRequestActionClass() - { - return SpecimenController2.ShowAPICreateSpecimenRequestAction.class; + return SpecimenController.ShowCreateSpecimenRequestAction.class; } @Override - public Class getExtendedSpecimenRequestActionClass() + public Class getUpdateCommentsActionClass() { - return SpecimenController2.ExtendedSpecimenRequestAction.class; + return SpecimenController.UpdateCommentsAction.class; } }); } @@ -243,15 +211,16 @@ protected void startupAfterSpringConfig(ModuleContext moduleContext) AuditLogService.get().registerAuditType(new SpecimenCommentAuditProvider()); SpecimenService.get().registerSpecimenTransform(new SampleMindedTransform()); PipelineService.get().registerPipelineProvider(new SpecimenPipeline(this)); + StudyInternalService.get().registerManageStudyViewFactory(ctx->ctx.getContainer().hasActiveModuleByName("specimen") ? new ManageSpecimenView() : null); SimpleStudyWriterRegistry.registerSimpleStudyWriterProvider(() -> List.of( - new SpecimenSettingsWriter(), - new SpecimenArchiveWriter() + new SpecimenArchiveWriter(), + new SpecimenSettingsWriter() )); SimpleStudyImporterRegistry.registerSimpleStudyImporterProvider(() -> List.of( - new SpecimenSettingsImporter(), - new SpecimenSchemaImporter() + new SpecimenSchemaImporter(), + new SpecimenSettingsImporter() )); } @@ -267,8 +236,8 @@ public Collection getSummary(Container c) public Set getUnitTests() { return Set.of( - SpecimenWriter.TestCase.class, - SampleMindedTransformTask.TestCase.class + SampleMindedTransformTask.TestCase.class, + SpecimenWriter.TestCase.class ); } diff --git a/specimen/src/org/labkey/specimen/SpecimenServiceImpl.java b/specimen/src/org/labkey/specimen/SpecimenServiceImpl.java index 96f3073f..cf80ff5d 100644 --- a/specimen/src/org/labkey/specimen/SpecimenServiceImpl.java +++ b/specimen/src/org/labkey/specimen/SpecimenServiceImpl.java @@ -53,7 +53,7 @@ import org.labkey.api.util.Pair; import org.labkey.api.view.ActionURL; import org.labkey.api.view.ViewBackgroundInfo; -import org.labkey.specimen.actions.SpecimenController2; +import org.labkey.specimen.actions.SpecimenController; import org.labkey.specimen.pipeline.SpecimenReloadJob; import org.springframework.web.servlet.mvc.Controller; @@ -467,6 +467,6 @@ public void fireSpecimensChanged(Container c, User user, Logger logger) @Override public Class getManageSpecimenWebPartActionClass() { - return SpecimenController2.ManageSpecimenWebPartAction.class; + return SpecimenController.ManageSpecimenWebPartAction.class; } } diff --git a/specimen/src/org/labkey/specimen/actions/HiddenFormInputGenerator.java b/specimen/src/org/labkey/specimen/actions/HiddenFormInputGenerator.java new file mode 100644 index 00000000..7f6bfa27 --- /dev/null +++ b/specimen/src/org/labkey/specimen/actions/HiddenFormInputGenerator.java @@ -0,0 +1,8 @@ +package org.labkey.specimen.actions; + +import org.labkey.api.view.ViewContext; + +public interface HiddenFormInputGenerator +{ + String getHiddenFormInputs(ViewContext ctx); +} diff --git a/specimen/src/org/labkey/specimen/actions/IdForm.java b/specimen/src/org/labkey/specimen/actions/IdForm.java new file mode 100644 index 00000000..33d62456 --- /dev/null +++ b/specimen/src/org/labkey/specimen/actions/IdForm.java @@ -0,0 +1,32 @@ +package org.labkey.specimen.actions; + +import org.labkey.api.action.ReturnUrlForm; + +public class IdForm extends ReturnUrlForm +{ + public enum PARAMS + { + id + } + + private int _id; + + public IdForm() + { + } + + public IdForm(int id) + { + _id = id; + } + + public int getId() + { + return _id; + } + + public void setId(int id) + { + _id = id; + } +} diff --git a/specimen/src/org/labkey/specimen/actions/IdTypes.java b/specimen/src/org/labkey/specimen/actions/IdTypes.java new file mode 100644 index 00000000..f8708571 --- /dev/null +++ b/specimen/src/org/labkey/specimen/actions/IdTypes.java @@ -0,0 +1,8 @@ +package org.labkey.specimen.actions; + +public enum IdTypes +{ + GlobalUniqueId, + SpecimenHash, + RowId +} diff --git a/specimen/src/org/labkey/specimen/actions/ManageRequestBean.java b/specimen/src/org/labkey/specimen/actions/ManageRequestBean.java new file mode 100644 index 00000000..b7dd56ce --- /dev/null +++ b/specimen/src/org/labkey/specimen/actions/ManageRequestBean.java @@ -0,0 +1,258 @@ +package org.labkey.specimen.actions; + +import org.labkey.api.data.ActionButton; +import org.labkey.api.data.Container; +import org.labkey.api.data.MenuButton; +import org.labkey.api.specimen.SpecimenRequestManager; +import org.labkey.api.specimen.SpecimenRequestStatus; +import org.labkey.api.specimen.Vial; +import org.labkey.api.specimen.location.LocationImpl; +import org.labkey.api.specimen.location.LocationManager; +import org.labkey.api.specimen.model.SpecimenRequestActor; +import org.labkey.specimen.notifications.ActorNotificationRecipientSet; +import org.labkey.api.specimen.requirements.SpecimenRequest; +import org.labkey.api.specimen.requirements.SpecimenRequestRequirement; +import org.labkey.api.specimen.requirements.SpecimenRequestRequirementProvider; +import org.labkey.api.specimen.security.permissions.ManageRequestsPermission; +import org.labkey.api.study.Location; +import org.labkey.api.util.Button; +import org.labkey.api.view.ActionURL; +import org.labkey.api.view.DisplayElement; +import org.labkey.api.view.ViewContext; +import org.labkey.specimen.actions.SpecimenController.ImportVialIdsAction; +import org.labkey.specimen.actions.SpecimenController.OverviewAction; +import org.labkey.specimen.actions.SpecimenController.RemoveRequestSpecimensAction; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public class ManageRequestBean extends SpecimensViewBean +{ + protected SpecimenRequest _specimenRequest; + protected boolean _requestManager; + protected boolean _requirementsComplete; + protected boolean _finalState; + private List _possibleNotifications; + protected List _missingSpecimens; + private final Boolean _submissionResult; + private Location[] _providingLocations; + private String _returnUrl; + private final Container _container; + + public ManageRequestBean(ViewContext context, SpecimenRequest specimenRequest, boolean forExport, Boolean submissionResult, String returnUrl) + { + super(context, specimenRequest.getVials(), !forExport, !forExport, forExport, false); + _container = context.getContainer(); + _submissionResult = submissionResult; + _requestManager = context.getContainer().hasPermission(context.getUser(), ManageRequestsPermission.class); + _specimenRequest = specimenRequest; + _finalState = SpecimenRequestManager.get().isInFinalState(_specimenRequest); + _requirementsComplete = true; + _missingSpecimens = SpecimenRequestManager.get().getMissingSpecimens(_specimenRequest); + _returnUrl = returnUrl; + SpecimenRequestRequirement[] requirements = specimenRequest.getRequirements(); + for (int i = 0; i < requirements.length && _requirementsComplete; i++) + { + SpecimenRequestRequirement requirement = requirements[i]; + _requirementsComplete = requirement.isComplete(); + } + + if (_specimenQueryView != null) + { + List buttons = new ArrayList<>(); + + MenuButton exportMenuButton = new MenuButton("Export"); + ActionURL exportExcelURL = context.getActionURL().clone().addParameter("export", "excel"); + ActionURL exportTextURL = context.getActionURL().clone().addParameter("export", "tsv"); + exportMenuButton.addMenuItem("Export all to Excel (.xls)", exportExcelURL); + exportMenuButton.addMenuItem("Export all to text file (.tsv)", exportTextURL); + buttons.add(exportMenuButton); + _specimenQueryView.setShowExportButtons(false); + _specimenQueryView.getSettings().setAllowChooseView(false); + + if (SpecimenRequestManager.get().hasEditRequestPermissions(context.getUser(), specimenRequest) && !_finalState) + { + ActionButton addButton = new ActionButton(new ActionURL(OverviewAction.class, _container), "Specimen Search"); + ActionButton deleteButton = new ActionButton(RemoveRequestSpecimensAction.class, "Remove Selected"); + _specimenQueryView.addHiddenFormField("id", "" + specimenRequest.getRowId()); + buttons.add(addButton); + + ActionURL importActionURL = new ActionURL(ImportVialIdsAction.class, _container); + importActionURL.addParameter("id", specimenRequest.getRowId()); + Button importButton = new Button.ButtonBuilder("Upload Specimen Ids") + .href(importActionURL) + .submit(false) + .build(); + buttons.add(importButton); + buttons.add(deleteButton); + } + _specimenQueryView.setButtons(buttons); + } + } + + public List getPossibleNotifications(SpecimenRequest specimenRequest) + { + List possibleNotifications = new ArrayList<>(); + // allow notification of all parties listed in the request requirements: + for (SpecimenRequestRequirement requirement : specimenRequest.getRequirements()) + addIfNotPresent(requirement.getActor(), requirement.getLocation(), possibleNotifications); + + // allow notification of all site-based actors at the destination site, and all study-wide actors: + Map relevantSites = new HashMap<>(); + if (specimenRequest.getDestinationSiteId() == null) + { + throw new IllegalStateException("Request " + specimenRequest.getRowId() + " in folder " + + specimenRequest.getContainer().getPath() + " does not have a valid destination site id."); + } + LocationImpl destLocation = LocationManager.get().getLocation(specimenRequest.getContainer(), specimenRequest.getDestinationSiteId().intValue()); + relevantSites.put(destLocation.getRowId(), destLocation); + for (Vial vial : specimenRequest.getVials()) + { + LocationImpl location = LocationManager.get().getCurrentLocation(vial); + if (location != null && !relevantSites.containsKey(location.getRowId())) + relevantSites.put(location.getRowId(), location); + } + + SpecimenRequestActor[] allActors = SpecimenRequestRequirementProvider.get().getActors(specimenRequest.getContainer()); + // add study-wide actors and actors from all relevant sites: + for (SpecimenRequestActor actor : allActors) + { + if (actor.isPerSite()) + { + for (LocationImpl location : relevantSites.values()) + { + if (actor.isPerSite()) + addIfNotPresent(actor, location, possibleNotifications); + } + } + else + addIfNotPresent(actor, null, possibleNotifications); + } + + possibleNotifications.sort((first, second) -> + { + String firstSite = first.getLocation() != null ? first.getLocation().getLabel() : ""; + String secondSite = second.getLocation() != null ? second.getLocation().getLabel() : ""; + int comp = firstSite.compareToIgnoreCase(secondSite); + if (comp == 0) + { + String firstActorLabel = first.getActor().getLabel(); + if (firstActorLabel == null) + firstActorLabel = ""; + String secondActorLabel = second.getActor().getLabel(); + if (secondActorLabel == null) + secondActorLabel = ""; + comp = firstActorLabel.compareToIgnoreCase(secondActorLabel); + } + return comp; + }); + return possibleNotifications; + } + + public synchronized List getPossibleNotifications() + { + if (_possibleNotifications == null) + _possibleNotifications = getPossibleNotifications(_specimenRequest); + return _possibleNotifications; + } + + public SpecimenRequest getSpecimenRequest() + { + return _specimenRequest; + } + + public boolean hasMissingSpecimens() + { + return _missingSpecimens != null && !_missingSpecimens.isEmpty(); + } + + public List getMissingSpecimens() + { + return _missingSpecimens; + } + + public SpecimenRequestStatus getStatus() + { + return SpecimenRequestManager.get().getRequestStatus(_specimenRequest.getContainer(), _specimenRequest.getStatusId()); + } + + public Location getDestinationSite() + { + Integer destinationSiteId = _specimenRequest.getDestinationSiteId(); + if (destinationSiteId != null) + { + return LocationManager.get().getLocation(_specimenRequest.getContainer(), destinationSiteId.intValue()); + } + return null; + } + + public boolean isRequestManager() + { + return _requestManager; + } + + public boolean isFinalState() + { + return _finalState; + } + + public boolean isRequirementsComplete() + { + return _requirementsComplete; + } + + public Boolean isSuccessfulSubmission() + { + return _submissionResult != null && _submissionResult.booleanValue(); + } + + public Location[] getProvidingLocations() + { + if (_providingLocations == null) + { + Set locationSet = new HashSet<>(); + for (Vial vial : _vials) + { + Integer locationId = vial.getCurrentLocation(); + if (locationId != null) + locationSet.add(locationId); + } + _providingLocations = new Location[locationSet.size()]; + int i = 0; + for (Integer locationId : locationSet) + _providingLocations[i++] = LocationManager.get().getLocation(_container, locationId); + } + return _providingLocations; + } + + public String getReturnUrl() + { + return _returnUrl; + } + + public void setReturnUrl(String returnUrl) + { + _returnUrl = returnUrl; + } + + private boolean addIfNotPresent(SpecimenRequestActor actor, LocationImpl location, List list) + { + for (ActorNotificationRecipientSet actorSite : list) + { + if (actorSite.getActor().getRowId() == actor.getRowId()) + { + if (actorSite.getLocation() == null && location == null) + return false; + else + if (actorSite.getLocation() != null && location != null && actorSite.getLocation().getRowId() == location.getRowId()) + return false; + } + } + list.add(new ActorNotificationRecipientSet(actor, location)); + return true; + } +} diff --git a/specimen/src/org/labkey/specimen/actions/ManageRequestInputsBean.java b/specimen/src/org/labkey/specimen/actions/ManageRequestInputsBean.java new file mode 100644 index 00000000..434e2ee3 --- /dev/null +++ b/specimen/src/org/labkey/specimen/actions/ManageRequestInputsBean.java @@ -0,0 +1,36 @@ +package org.labkey.specimen.actions; + +import org.labkey.api.data.Container; +import org.labkey.api.specimen.SpecimenRequestManager; +import org.labkey.api.view.ViewContext; + +import java.sql.SQLException; + +public class ManageRequestInputsBean +{ + private final SpecimenRequestManager.SpecimenRequestInput[] _inputs; + private final Container _container; + private final String _contextPath; + + public ManageRequestInputsBean(ViewContext context) throws SQLException + { + _container = context.getContainer(); + _inputs = SpecimenRequestManager.get().getNewSpecimenRequestInputs(_container); + _contextPath = context.getContextPath(); + } + + public SpecimenRequestManager.SpecimenRequestInput[] getInputs() + { + return _inputs; + } + + public Container getContainer() + { + return _container; + } + + public String getContextPath() + { + return _contextPath; + } +} diff --git a/specimen/src/org/labkey/specimen/actions/SelectSpecimenProviderBean.java b/specimen/src/org/labkey/specimen/actions/SelectSpecimenProviderBean.java new file mode 100644 index 00000000..2e371255 --- /dev/null +++ b/specimen/src/org/labkey/specimen/actions/SelectSpecimenProviderBean.java @@ -0,0 +1,33 @@ +package org.labkey.specimen.actions; + +import org.labkey.api.specimen.location.LocationImpl; +import org.labkey.api.view.ActionURL; + +public class SelectSpecimenProviderBean +{ + private final HiddenFormInputGenerator _sourceForm; + private final LocationImpl[] _possibleLocations; + private final ActionURL _formTarget; + + public SelectSpecimenProviderBean(HiddenFormInputGenerator sourceForm, LocationImpl[] possibleLocations, ActionURL formTarget) + { + _sourceForm = sourceForm; + _possibleLocations = possibleLocations; + _formTarget = formTarget; + } + + public LocationImpl[] getPossibleLocations() + { + return _possibleLocations; + } + + public ActionURL getFormTarget() + { + return _formTarget; + } + + public HiddenFormInputGenerator getSourceForm() + { + return _sourceForm; + } +} diff --git a/specimen/src/org/labkey/specimen/actions/ShowGroupMembersAction.java b/specimen/src/org/labkey/specimen/actions/ShowGroupMembersAction.java index 5abde517..3eaef996 100644 --- a/specimen/src/org/labkey/specimen/actions/ShowGroupMembersAction.java +++ b/specimen/src/org/labkey/specimen/actions/ShowGroupMembersAction.java @@ -16,8 +16,10 @@ package org.labkey.specimen.actions; import org.apache.commons.lang3.StringUtils; +import org.jetbrains.annotations.Nullable; import org.labkey.api.action.FormViewAction; import org.labkey.api.action.SpringActionController; +import org.labkey.api.data.Container; import org.labkey.api.security.RequiresPermission; import org.labkey.api.security.SecurityManager; import org.labkey.api.security.SecurityUrls; @@ -25,19 +27,18 @@ import org.labkey.api.security.UserManager; import org.labkey.api.security.ValidEmail; import org.labkey.api.security.permissions.UserManagementPermission; -import org.labkey.api.specimen.actions.IdForm; import org.labkey.api.specimen.location.LocationImpl; import org.labkey.api.specimen.location.LocationManager; import org.labkey.api.specimen.model.SpecimenRequestActor; import org.labkey.api.specimen.requirements.SpecimenRequestRequirementProvider; -import org.labkey.api.specimen.security.permissions.ManageSpecimenActorsPermission; -import org.labkey.api.study.SpecimenUrls; +import org.labkey.specimen.security.permissions.ManageSpecimenActorsPermission; import org.labkey.api.study.StudyUrls; import org.labkey.api.util.PageFlowUtil; import org.labkey.api.view.ActionURL; import org.labkey.api.view.JspView; import org.labkey.api.view.NavTree; import org.labkey.api.view.NotFoundException; +import org.labkey.specimen.actions.SpecimenController.ManageActorsAction; import org.springframework.validation.BindException; import org.springframework.validation.Errors; import org.springframework.web.servlet.ModelAndView; @@ -52,6 +53,18 @@ public class ShowGroupMembersAction extends FormViewAction 0) @@ -89,7 +93,7 @@ public void addNavTrail(NavTree root) { setHelpTopic("specimenShopping"); root.addChild(_study.getLabel(), PageFlowUtil.urlProvider(StudyUrls.class).getStudyOverviewURL(getContainer())); - root.addChild("Specimen Overview", new ActionURL(SpecimenController2.OverviewAction.class, getContainer())); + root.addChild("Specimen Overview", new ActionURL(SpecimenController.OverviewAction.class, getContainer())); root.addChild(_title); } diff --git a/specimen/src/org/labkey/specimen/actions/ShowUploadSpecimensAction.java b/specimen/src/org/labkey/specimen/actions/ShowUploadSpecimensAction.java index 38f791d4..edaa1675 100644 --- a/specimen/src/org/labkey/specimen/actions/ShowUploadSpecimensAction.java +++ b/specimen/src/org/labkey/specimen/actions/ShowUploadSpecimensAction.java @@ -34,7 +34,6 @@ import org.labkey.api.specimen.importer.SimpleSpecimenImporter; import org.labkey.api.specimen.settings.RepositorySettings; import org.labkey.api.specimen.settings.SettingsManager; -import org.labkey.api.study.SpecimenUrls; import org.labkey.api.study.Study; import org.labkey.api.study.StudyService; import org.labkey.api.study.StudyUrls; @@ -297,7 +296,7 @@ public void addNavTrail(NavTree root) Study s = StudyService.get().getStudy(getContainer()); root.addChild(s.getLabel(), PageFlowUtil.urlProvider(StudyUrls.class).getStudyOverviewURL(getContainer())); - root.addChild("Specimen Overview", new ActionURL(SpecimenController2.OverviewAction.class, getContainer())); + root.addChild("Specimen Overview", new ActionURL(SpecimenController.OverviewAction.class, getContainer())); root.addChild("Upload Specimens"); } @@ -308,7 +307,7 @@ public static class ImportCompleteAction extends SimpleViewAction public ModelAndView getView(Object o, BindException errors) { ActionURL homeLink = PageFlowUtil.urlProvider(ProjectUrls.class).getStartURL(getContainer()); - ActionURL samplesLink = PageFlowUtil.urlProvider(SpecimenUrls.class).getSpecimensURL(getContainer(), true); + ActionURL samplesLink = SpecimenController.getSpecimensURL(getContainer(), true); return new HtmlView("Specimens uploaded successfully.

" + PageFlowUtil.textLink("study home", homeLink) + " " + PageFlowUtil.textLink("specimens", samplesLink)); @@ -320,7 +319,7 @@ public void addNavTrail(NavTree root) Study study = StudyService.get().getStudy(getContainer()); root.addChild(study.getLabel(), PageFlowUtil.urlProvider(StudyUrls.class).getStudyOverviewURL(getContainer())); - root.addChild("Specimen Overview", new ActionURL(SpecimenController2.OverviewAction.class, getContainer())); + root.addChild("Specimen Overview", new ActionURL(SpecimenController.OverviewAction.class, getContainer())); root.addChild("Sample Import Complete"); } } diff --git a/specimen/src/org/labkey/specimen/actions/SpecimenApiController.java b/specimen/src/org/labkey/specimen/actions/SpecimenApiController.java index 3804eb83..6bd96f7b 100644 --- a/specimen/src/org/labkey/specimen/actions/SpecimenApiController.java +++ b/specimen/src/org/labkey/specimen/actions/SpecimenApiController.java @@ -36,7 +36,6 @@ import org.labkey.api.specimen.SpecimenRequestManager; import org.labkey.api.specimen.SpecimenRequestStatus; import org.labkey.api.specimen.Vial; -import org.labkey.api.specimen.actions.IdTypes; import org.labkey.api.specimen.importer.RequestabilityManager; import org.labkey.api.specimen.location.LocationImpl; import org.labkey.api.specimen.location.LocationManager; diff --git a/specimen/src/org/labkey/specimen/actions/SpecimenController.java b/specimen/src/org/labkey/specimen/actions/SpecimenController.java new file mode 100644 index 00000000..6e645c61 --- /dev/null +++ b/specimen/src/org/labkey/specimen/actions/SpecimenController.java @@ -0,0 +1,5622 @@ +package org.labkey.specimen.actions; + +import org.apache.commons.lang3.StringUtils; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.json.JSONObject; +import org.labkey.api.action.ApiResponse; +import org.labkey.api.action.ApiSimpleResponse; +import org.labkey.api.action.FormHandlerAction; +import org.labkey.api.action.FormViewAction; +import org.labkey.api.action.HasViewContext; +import org.labkey.api.action.MutatingApiAction; +import org.labkey.api.action.QueryViewAction; +import org.labkey.api.action.ReadOnlyApiAction; +import org.labkey.api.action.ReturnUrlForm; +import org.labkey.api.action.SimpleRedirectAction; +import org.labkey.api.action.SimpleViewAction; +import org.labkey.api.action.SpringActionController; +import org.labkey.api.attachments.Attachment; +import org.labkey.api.attachments.AttachmentFile; +import org.labkey.api.attachments.AttachmentParent; +import org.labkey.api.attachments.AttachmentService; +import org.labkey.api.attachments.BaseDownloadAction; +import org.labkey.api.attachments.ByteArrayAttachmentFile; +import org.labkey.api.audit.TransactionAuditProvider; +import org.labkey.api.data.*; +import org.labkey.api.gwt.client.AuditBehaviorType; +import org.labkey.api.module.FolderType; +import org.labkey.api.pipeline.PipeRoot; +import org.labkey.api.pipeline.PipelineService; +import org.labkey.api.pipeline.PipelineStatusUrls; +import org.labkey.api.pipeline.browse.PipelinePathForm; +import org.labkey.api.query.AbstractQueryImportAction; +import org.labkey.api.query.BatchValidationException; +import org.labkey.api.query.CustomView; +import org.labkey.api.query.FieldKey; +import org.labkey.api.query.QueryDefinition; +import org.labkey.api.query.QueryService; +import org.labkey.api.query.ValidationException; +import org.labkey.api.reader.ColumnDescriptor; +import org.labkey.api.reader.DataLoader; +import org.labkey.api.security.ActionNames; +import org.labkey.api.security.RequiresPermission; +import org.labkey.api.security.RequiresSiteAdmin; +import org.labkey.api.security.User; +import org.labkey.api.security.ValidEmail; +import org.labkey.api.security.permissions.AdminPermission; +import org.labkey.api.security.permissions.ReadPermission; +import org.labkey.api.specimen.AmbiguousLocationException; +import org.labkey.api.specimen.RequestEventType; +import org.labkey.api.specimen.RequestedSpecimens; +import org.labkey.api.specimen.SpecimenManager; +import org.labkey.api.specimen.SpecimenManagerNew; +import org.labkey.api.specimen.SpecimenMigrationService; +import org.labkey.api.specimen.SpecimenQuerySchema; +import org.labkey.api.specimen.SpecimenRequestException; +import org.labkey.api.specimen.SpecimenRequestManager; +import org.labkey.api.specimen.SpecimenRequestStatus; +import org.labkey.api.specimen.SpecimenSchema; +import org.labkey.api.specimen.Vial; +import org.labkey.api.specimen.actions.ParticipantCommentForm; +import org.labkey.api.specimen.importer.RequestabilityManager; +import org.labkey.api.specimen.importer.SimpleSpecimenImporter; +import org.labkey.api.specimen.location.LocationImpl; +import org.labkey.api.specimen.location.LocationManager; +import org.labkey.api.specimen.model.ExtendedSpecimenRequestView; +import org.labkey.api.specimen.model.SpecimenComment; +import org.labkey.api.specimen.model.SpecimenRequestActor; +import org.labkey.api.specimen.model.SpecimenRequestEvent; +import org.labkey.api.specimen.notifications.DefaultRequestNotification; +import org.labkey.api.specimen.notifications.NotificationRecipientSet; +import org.labkey.api.specimen.query.SpecimenQueryView; +import org.labkey.api.specimen.requirements.SpecimenRequest; +import org.labkey.api.specimen.requirements.SpecimenRequestRequirement; +import org.labkey.api.specimen.requirements.SpecimenRequestRequirementProvider; +import org.labkey.api.specimen.requirements.SpecimenRequestRequirementType; +import org.labkey.api.specimen.security.permissions.ManageRequestSettingsPermission; +import org.labkey.api.specimen.security.permissions.ManageRequestsPermission; +import org.labkey.api.specimen.security.permissions.RequestSpecimensPermission; +import org.labkey.specimen.security.permissions.SetSpecimenCommentsPermission; +import org.labkey.api.specimen.settings.DisplaySettings; +import org.labkey.api.specimen.settings.RepositorySettings; +import org.labkey.api.specimen.settings.RequestNotificationSettings; +import org.labkey.api.specimen.settings.SettingsManager; +import org.labkey.api.specimen.settings.StatusSettings; +import org.labkey.api.specimen.view.NotificationBean; +import org.labkey.api.specimen.view.SpecimenRequestNotificationEmailTemplate; +import org.labkey.api.study.CohortFilter; +import org.labkey.api.study.Dataset; +import org.labkey.api.study.MapArrayExcelWriter; +import org.labkey.api.study.SpecimenService; +import org.labkey.api.study.SpecimenTransform; +import org.labkey.api.study.SpecimenUrls; +import org.labkey.api.study.Study; +import org.labkey.api.study.StudyInternalService; +import org.labkey.api.study.StudyService; +import org.labkey.api.study.StudyUrls; +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.ParticipantDataset; +import org.labkey.api.study.security.permissions.ManageStudyPermission; +import org.labkey.api.util.Button; +import org.labkey.api.util.ConfigurationException; +import org.labkey.api.util.DateUtil; +import org.labkey.api.util.ExceptionUtil; +import org.labkey.api.util.FileStream; +import org.labkey.api.util.GUID; +import org.labkey.api.util.HelpTopic; +import org.labkey.api.util.Link; +import org.labkey.api.util.MailHelper; +import org.labkey.api.util.PageFlowUtil; +import org.labkey.api.util.Pair; +import org.labkey.api.util.URLHelper; +import org.labkey.api.util.emailTemplate.EmailTemplateService; +import org.labkey.api.view.ActionURL; +import org.labkey.api.view.AjaxCompletion; +import org.labkey.api.view.DisplayElement; +import org.labkey.api.view.GridView; +import org.labkey.api.view.HBox; +import org.labkey.api.view.HtmlView; +import org.labkey.api.view.JspView; +import org.labkey.api.view.NavTree; +import org.labkey.api.view.NotFoundException; +import org.labkey.api.view.RedirectException; +import org.labkey.api.view.UnauthorizedException; +import org.labkey.api.view.VBox; +import org.labkey.api.view.ViewBackgroundInfo; +import org.labkey.api.view.ViewContext; +import org.labkey.api.view.WebPartView; +import org.labkey.specimen.notifications.ActorNotificationRecipientSet; +import org.labkey.specimen.pipeline.SpecimenArchive; +import org.labkey.specimen.pipeline.SpecimenBatch; +import org.labkey.specimen.query.SpecimenEventQueryView; +import org.labkey.specimen.query.SpecimenRequestQueryView; +import org.labkey.specimen.security.permissions.ManageDisplaySettingsPermission; +import org.labkey.specimen.security.permissions.ManageNewRequestFormPermission; +import org.labkey.specimen.security.permissions.ManageNotificationsPermission; +import org.labkey.specimen.security.permissions.ManageRequestRequirementsPermission; +import org.labkey.specimen.security.permissions.ManageRequestStatusesPermission; +import org.labkey.specimen.security.permissions.ManageSpecimenActorsPermission; +import org.labkey.specimen.view.SpecimenSearchWebPart; +import org.labkey.specimen.view.SpecimenWebPart; +import org.springframework.validation.BindException; +import org.springframework.validation.Errors; +import org.springframework.validation.ObjectError; +import org.springframework.web.servlet.ModelAndView; +import org.springframework.web.servlet.mvc.Controller; + +import javax.mail.Address; +import javax.mail.Message; +import javax.mail.internet.InternetAddress; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpSession; +import java.io.BufferedOutputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.OutputStream; +import java.io.Writer; +import java.nio.charset.StandardCharsets; +import java.sql.SQLException; +import java.sql.Timestamp; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public class SpecimenController extends SpringActionController +{ + private static final DefaultActionResolver _resolver = new DefaultActionResolver( + SpecimenController.class, + + ShowGroupMembersAction.class, + ShowSearchAction.class, + ShowUploadSpecimensAction.class, + ShowUploadSpecimensAction.ImportCompleteAction.class, + + // Report actions from SpecimenReportActions + SpecimenReportActions.ParticipantSummaryReportAction.class, + SpecimenReportActions.ParticipantTypeReportAction.class, + SpecimenReportActions.ParticipantSiteReportAction.class, + SpecimenReportActions.RequestReportAction.class, + SpecimenReportActions.RequestEnrollmentSiteReportAction.class, + SpecimenReportActions.RequestSiteReportAction.class, + SpecimenReportActions.RequestParticipantReportAction.class, + SpecimenReportActions.TypeParticipantReportAction.class, + SpecimenReportActions.TypeSummaryReportAction.class, + SpecimenReportActions.TypeCohortReportAction.class + ); + + private Study _study = null; + + public SpecimenController() + { + setActionResolver(_resolver); + } + + @Nullable + public Study getStudy() + { + if (null == _study) + _study = StudyService.get().getStudy(getContainer()); + return _study; + } + + @NotNull + public Study getStudyThrowIfNull() throws IllegalStateException + { + Study study = StudyService.get().getStudy(getContainer()); + if (null == study) + { + // We expected to find a study + throw new NotFoundException("No study found."); + } + return study; + } + + @NotNull + public Study getStudyRedirectIfNull() + { + return getStudyRedirectIfNull(getContainer()); + } + + @NotNull + public static Study getStudyRedirectIfNull(Container c) + { + Study study = StudyService.get().getStudy(c); + if (null == study) + { + // redirect to the study home page, where admins will see a 'create study' button, + // and non-admins will simply see a message that no study exists. + throw new RedirectException(urlProvider(StudyUrls.class).getBeginURL(c)); + } + return study; + } + + public void addRootNavTrail(NavTree root) + { + Study study = getStudyRedirectIfNull(); + Container c = getContainer(); + ActionURL rootURL; + FolderType folderType = c.getFolderType(); + if ("study".equals(folderType.getDefaultModule().getName())) + { + rootURL = folderType.getStartURL(c, getUser()); + } + else + { + rootURL = urlProvider(StudyUrls.class).getBeginURL(c); + } + root.addChild(study.getLabel(), rootURL); + } + + private void addBaseSpecimenNavTrail(NavTree root) + { + addRootNavTrail(root); + ActionURL overviewURL = new ActionURL(OverviewAction.class, getContainer()); + root.addChild("Specimen Overview", overviewURL); + } + + private void addSpecimenRequestsNavTrail(NavTree root) + { + addBaseSpecimenNavTrail(root); + root.addChild("Specimen Requests", new ActionURL(ViewRequestsAction.class, getContainer())); + } + + private void addSpecimenRequestNavTrail(NavTree root, int requestId) + { + addSpecimenRequestsNavTrail(root); + root.addChild("Specimen Request " + requestId, getManageRequestURL(requestId, null)); + } + + private void addManageStudyNavTrail(NavTree root) + { + urlProvider(StudyUrls.class).addManageStudyNavTrail(root, getContainer(), getUser()); + } + + private ActionURL getManageStudyURL() + { + return urlProvider(StudyUrls.class).getManageStudyURL(getContainer()); + } + + public void ensureSpecimenRequestsConfigured(boolean checkExistingStatuses) + { + if (!SettingsManager.get().isSpecimenRequestEnabled(getContainer(), checkExistingStatuses)) + throw new RedirectException(new ActionURL(SpecimenRequestConfigRequiredAction.class, getContainer())); + } + + public static boolean nullSafeEqual(T first, T second) + { + if (first == null && second == null) + return true; + if (first == null) + return false; + return first.equals(second); + } + + private ActionURL urlFor(Class action) + { + return new ActionURL(action, getContainer()); + } + + public boolean isCommentsMode(Container container, SpecimenQueryView.Mode selectedMode) + { + return (selectedMode == SpecimenQueryView.Mode.COMMENTS) || + (selectedMode == SpecimenQueryView.Mode.DEFAULT && SettingsManager.get().getDisplaySettings(container).isDefaultToCommentsMode()); + } + + public SpecimenQueryView getSpecimenQueryView(boolean showVials, boolean forExport, @Nullable Collection cachedFilterData, SpecimenQueryView.Mode viewMode, CohortFilter cohortFilter) + { + boolean commentsMode = isCommentsMode(getContainer(), viewMode); + + SpecimenQueryView gridView; + RepositorySettings settings = SettingsManager.get().getRepositorySettings(getContainer()); + + if (cachedFilterData != null) + { + gridView = SpecimenQueryView.createView(getViewContext(), cachedFilterData, + showVials ? SpecimenQueryView.ViewType.VIALS : + SpecimenQueryView.ViewType.SUMMARY); + } + else + { + gridView = SpecimenQueryView.createView(getViewContext(), + showVials ? SpecimenQueryView.ViewType.VIALS : + SpecimenQueryView.ViewType.SUMMARY, cohortFilter); + } + gridView.setShowHistoryLinks(showVials && !forExport && !settings.isSimple()); + gridView.setDisableLowVialIndicators(forExport || settings.isSimple()); + gridView.setShowRecordSelectors(settings.isEnableRequests() || commentsMode); // Need to allow buttons + gridView.setShowDeleteButton(false); + + // if we're exporting, we can skip setting up the buttons: + if (forExport) + return gridView; + + List buttons = new ArrayList<>(); + + ActionButton ptidListButton = StudyInternalService.get().createParticipantGroupButton(getViewContext(), gridView.getSettings().getDataRegionName(), cohortFilter, false); + if (ptidListButton != null) + buttons.add(ptidListButton); + + if (settings.isEnableRequests()) + { + MenuButton requestMenuButton = new MenuButton("Request Options"); + requestMenuButton.addMenuItem("View Existing Requests", SpecimenMigrationService.get().getViewRequestsURL(getContainer())); + if (!commentsMode) + { + if (getViewContext().getContainer().hasPermission(getViewContext().getUser(), RequestSpecimensPermission.class)) + { + final String jsRegionObject = DataRegion.getJavaScriptObjectReference(gridView.getSettings().getDataRegionName()); + String createRequestURL = urlFor(SpecimenMigrationService.get().getShowCreateSpecimenRequestActionClass()).addReturnURL(getViewContext().getActionURL()).toString(); + + requestMenuButton.addMenuItem("Create New Request", + "if (verifySelected(" + jsRegionObject + ".form, '" + createRequestURL + + "', 'post', 'rows')) " + jsRegionObject + ".form.submit();"); + + if (getViewContext().getContainer().hasPermission(getViewContext().getUser(), ManageRequestsPermission.class) || + SettingsManager.get().isSpecimenShoppingCartEnabled(getViewContext().getContainer())) + { + requestMenuButton.addMenuItem("Add To Existing Request", + "if (verifySelected(" + jsRegionObject + ".form, '#', " + + "'get', 'rows')) { " + jsRegionObject + ".getSelected({success: function (data) { showRequestWindow(data.selected, '" + (showVials ? IdTypes.RowId + : IdTypes.SpecimenHash) + "');}})}"); + } + } + } + else + { + ActionURL endCommentsURL = getViewContext().getActionURL().clone(); + endCommentsURL.replaceParameter(SpecimenViewTypeForm.PARAMS.viewMode, SpecimenQueryView.Mode.REQUESTS.name()); + requestMenuButton.addMenuItem("Enable Request Mode", endCommentsURL); + } + + buttons.add(requestMenuButton); + } + + if (getViewContext().getContainer().hasPermission(getUser(), SetSpecimenCommentsPermission.class)) + { + boolean manualQCEnabled = SettingsManager.get().getDisplaySettings(getViewContext().getContainer()).isEnableManualQCFlagging(); + if (commentsMode) + { + MenuButton commentsMenuButton = new MenuButton("Comments" + (manualQCEnabled ? " and QC" : "")); + final String jsRegionObject = DataRegion.getJavaScriptObjectReference(gridView.getSettings().getDataRegionName()); + String setCommentsURL = urlFor(SpecimenMigrationService.get().getUpdateCommentsActionClass()).toString(); + NavTree setItem = commentsMenuButton.addMenuItem("Set Vial Comment " + (manualQCEnabled ? "or QC State " : "") + "for Selected", + "if (verifySelected(" + jsRegionObject + ".form, '" + setCommentsURL + + "', 'post', 'rows')) " + jsRegionObject + ".form.submit(); return false;"); + setItem.setId("Comments:Set"); + + String clearCommentsURL = urlFor(SpecimenMigrationService.get().getClearCommentsActionClass()).toString(); + NavTree clearItem = commentsMenuButton.addMenuItem("Clear Vial Comments for Selected", + "if (verifySelected(" + jsRegionObject + ".form, '" + clearCommentsURL + + "', 'post', 'rows') && confirm('This will permanently clear comments for all selected vials. " + + (manualQCEnabled ? "Quality control states will remain unchanged. " : "" )+ "Continue?')) " + + jsRegionObject + ".form.submit();\nreturn false;"); + clearItem.setId("Comments:Clear"); + + ActionURL endCommentsURL = getViewContext().getActionURL().clone(); + endCommentsURL.replaceParameter(SpecimenViewTypeForm.PARAMS.viewMode, SpecimenQueryView.Mode.REQUESTS.name()); + NavTree exitItem = commentsMenuButton.addMenuItem("Exit Comments " + (manualQCEnabled ? "and QC " : "") + "mode", endCommentsURL); + exitItem.setId("Comments:Exit"); + + Study study = getStudy(); + String subjectNoun = StudyService.get().getSubjectNounSingular(getContainer()); + boolean addSep = true; + + Integer participantCommentDatasetId = StudyInternalService.get().getParticipantCommentDatasetId(study); + + if (participantCommentDatasetId != null && participantCommentDatasetId != -1) + { + Dataset ds = StudyService.get().getDataset(study.getContainer(), participantCommentDatasetId); + if (ds != null && ds.canUpdate(getUser())) + { + if (addSep) + { + commentsMenuButton.addSeparator(); + addSep = false; + } + NavTree ptidComments = commentsMenuButton.addMenuItem("Manage " + PageFlowUtil.filter(subjectNoun) + " Comments", + urlProvider(StudyUrls.class).getDatasetURL(getContainer(), participantCommentDatasetId)); + ptidComments.setId("Comments:SetParticipant"); + } + } + + Integer participantVisitCommentDatasetId = StudyInternalService.get().getParticipantVisitCommentDatasetId(study); + + if (participantVisitCommentDatasetId != null && participantVisitCommentDatasetId != -1) + { + Dataset ds = StudyService.get().getDataset(study.getContainer(), participantVisitCommentDatasetId); + if (ds != null && ds.canUpdate(getUser())) + { + if (addSep) + { + commentsMenuButton.addSeparator(); + addSep = false; + } + NavTree ptidComments = commentsMenuButton.addMenuItem("Manage " + PageFlowUtil.filter(subjectNoun) + "/Visit Comments", + urlProvider(StudyUrls.class).getDatasetURL(getContainer(), participantVisitCommentDatasetId)); + ptidComments.setId("Comments:SetParticipantVisit"); + } + } + buttons.add(commentsMenuButton); + } + else + { + ActionURL enableCommentsURL = getViewContext().getActionURL().clone(); + enableCommentsURL.replaceParameter(SpecimenViewTypeForm.PARAMS.viewMode, SpecimenQueryView.Mode.COMMENTS.name()); + ActionButton commentsButton = new ActionButton("Enable Comments" + (manualQCEnabled ? "/QC" : ""), enableCommentsURL); + buttons.add(commentsButton); + } + } + + if (getViewContext().hasPermission(AdminPermission.class)) + { + Button upload = new Button.ButtonBuilder("Import Specimens") + .href(SpecimenMigrationService.get().getUploadSpecimensURL(getContainer())) + .build(); + buttons.add(upload); + } + + if (settings.isEnableRequests() && getViewContext().getContainer().hasPermission(getViewContext().getUser(), RequestSpecimensPermission.class)) + { + buttons.add(new ButtonBarLineBreak()); + buttons.add(new ExcludeSiteDropDown()); + } + + gridView.setButtons(buttons); + return gridView; + } + + public static final class ExcludeSiteDropDown extends DisplayElement + { + @Override + public void render(RenderContext ctx, Writer out) throws IOException + { + ActionURL url = ctx.getViewContext().cloneActionURL(); + url.deleteParameter(SpecimenQueryView.PARAMS.excludeRequestedBySite.name()); + out.write("Hide Previously Requested By "); + out.write(""); + } + } + + public GridView getRequestEventGridView(HttpServletRequest request, BindException errors, SimpleFilter filter) + { + DataRegion rgn = new DataRegion(); + TableInfo tableInfoRequestEvent = SpecimenSchema.get().getTableInfoSampleRequestEvent(); + rgn.setTable(tableInfoRequestEvent); + rgn.setColumns(tableInfoRequestEvent.getColumns("Created", "EntryType", "Comments", "CreatedBy", "EntityId")); + rgn.getDisplayColumn("EntityId").setVisible(false); + + DataColumn commentsColumn = (DataColumn) rgn.getDisplayColumn("Comments"); + commentsColumn.setWidth("50%"); + commentsColumn.setPreserveNewlines(true); + rgn.addDisplayColumn(new AttachmentDisplayColumn(request)); + GridView grid = new GridView(rgn, errors); + grid.setFilter(filter); + grid.setSort(new Sort("Created")); + return grid; + } + + private static boolean addIfNotPresent(SpecimenRequestActor actor, LocationImpl location, List list) + { + for (ActorNotificationRecipientSet actorSite : list) + { + if (actorSite.getActor().getRowId() == actor.getRowId()) + { + if (actorSite.getLocation() == null && location == null) + return false; + else + if (actorSite.getLocation() != null && location != null && actorSite.getLocation().getRowId() == location.getRowId()) + return false; + } + } + list.add(new ActorNotificationRecipientSet(actor, location)); + return true; + } + + private static List getPossibleNotifications(SpecimenRequest specimenRequest) + { + List possibleNotifications = new ArrayList<>(); + // allow notification of all parties listed in the request requirements: + for (SpecimenRequestRequirement requirement : specimenRequest.getRequirements()) + addIfNotPresent(requirement.getActor(), requirement.getLocation(), possibleNotifications); + + // allow notification of all site-based actors at the destination site, and all study-wide actors: + Map relevantSites = new HashMap<>(); + if (specimenRequest.getDestinationSiteId() == null) + { + throw new IllegalStateException("Request " + specimenRequest.getRowId() + " in folder " + + specimenRequest.getContainer().getPath() + " does not have a valid destination site id."); + } + LocationImpl destLocation = LocationManager.get().getLocation(specimenRequest.getContainer(), specimenRequest.getDestinationSiteId().intValue()); + relevantSites.put(destLocation.getRowId(), destLocation); + for (Vial vial : specimenRequest.getVials()) + { + LocationImpl location = LocationManager.get().getCurrentLocation(vial); + if (location != null && !relevantSites.containsKey(location.getRowId())) + relevantSites.put(location.getRowId(), location); + } + + SpecimenRequestActor[] allActors = SpecimenRequestRequirementProvider.get().getActors(specimenRequest.getContainer()); + // add study-wide actors and actors from all relevant sites: + for (SpecimenRequestActor actor : allActors) + { + if (actor.isPerSite()) + { + for (LocationImpl location : relevantSites.values()) + { + if (actor.isPerSite()) + addIfNotPresent(actor, location, possibleNotifications); + } + } + else + addIfNotPresent(actor, null, possibleNotifications); + } + + possibleNotifications.sort((first, second) -> + { + String firstSite = first.getLocation() != null ? first.getLocation().getLabel() : ""; + String secondSite = second.getLocation() != null ? second.getLocation().getLabel() : ""; + int comp = firstSite.compareToIgnoreCase(secondSite); + if (comp == 0) + { + String firstActorLabel = first.getActor().getLabel(); + if (firstActorLabel == null) + firstActorLabel = ""; + String secondActorLabel = second.getActor().getLabel(); + if (secondActorLabel == null) + secondActorLabel = ""; + comp = firstActorLabel.compareToIgnoreCase(secondActorLabel); + } + return comp; + }); + return possibleNotifications; + } + + private String getShortSiteLabel(LocationImpl location) + { + String label; + if (location.getLabel() != null && location.getLabel().length() > 0) + label = location.getLabel().substring(0, Math.min(location.getLabel().length(), 15)); + else if (location.getLdmsLabCode() != null) + label = "ldmsId" + location.getLdmsLabCode(); + else if (location.getLabwareLabCode() != null && location.getLabwareLabCode().length() > 0) + label = "labwareId" + location.getLabwareLabCode(); + else + label = "rowId" + location.getRowId(); + return label.replaceAll("\\W+", "_"); + } + + private String getSpecimenListFileName(LocationImpl srcLocation, LocationImpl destLocation) + { + StringBuilder filename = new StringBuilder(); + filename.append(getShortSiteLabel(srcLocation)).append("_to_").append(getShortSiteLabel(destLocation)); + filename.append("_").append(DateUtil.formatDateISO8601()); + return filename.toString(); + } + + public SimpleFilter getSpecimenListFilter(SpecimenRequest specimenRequest, LocationImpl srcLocation, LabSpecimenListsBean.Type type) + { + LabSpecimenListsBean bean = new LabSpecimenListsBean(specimenRequest, type); + List vials = bean.getSpecimens(srcLocation); + Object[] params = new Object[vials.size() + 1]; + params[params.length - 1] = specimenRequest.getContainer().getId(); + StringBuilder whereClause = new StringBuilder(); + whereClause.append("("); + int i = 0; + for (Vial vial : vials) + { + if (i > 0) + whereClause.append(" OR "); + whereClause.append("RowId = ?"); + params[i++] = vial.getRowId(); + } + whereClause.append(") AND Container = ?"); + + SimpleFilter filter = new SimpleFilter(); + filter.addWhereClause(whereClause.toString(), params, FieldKey.fromParts("RowId"), FieldKey.fromParts("Container")); + return filter; + } + + public TSVGridWriter getSpecimenListTsvWriter(SpecimenRequest specimenRequest, LocationImpl srcLocation, + LocationImpl destLocation, LabSpecimenListsBean.Type type) + { + DataRegion dr = createDataRegionForWriters(specimenRequest); + RenderContext ctx = new RenderContext(getViewContext()); + ctx.setContainer(specimenRequest.getContainer()); + ctx.setBaseFilter(getSpecimenListFilter(specimenRequest, srcLocation, type)); + List cols = dr.getDisplayColumns(); + TSVGridWriter tsv = new TSVGridWriter(()->dr.getResults(ctx), cols); + tsv.setFilenamePrefix(getSpecimenListFileName(srcLocation, destLocation)); + return tsv; + } + + public ExcelWriter getSpecimenListXlsWriter(SpecimenRequest specimenRequest, LocationImpl srcLocation, + LocationImpl destLocation, LabSpecimenListsBean.Type type) + { + DataRegion dr = createDataRegionForWriters(specimenRequest); + RenderContext ctx = new RenderContext(getViewContext()); + ctx.setContainer(specimenRequest.getContainer()); + ctx.setBaseFilter(getSpecimenListFilter(specimenRequest, srcLocation, type)); + List cols = dr.getDisplayColumns(); + ExcelWriter xl = new ExcelWriter(() -> dr.getResults(ctx), cols); + xl.setFilenamePrefix(getSpecimenListFileName(srcLocation, destLocation)); + return xl; + } + + private DataRegion createDataRegionForWriters(SpecimenRequest specimenRequest) + { + DataRegion dr = new DataRegion(); + Container container = specimenRequest.getContainer(); + SpecimenQuerySchema querySchema = SpecimenQuerySchema.get(StudyService.get().getStudy(container), getViewContext().getUser()); + TableInfo table = querySchema.getTable(SpecimenQuerySchema.LOCATION_SPECIMEN_LIST_TABLE_NAME, true); + QueryDefinition queryDef = querySchema.getQueryDefForTable(SpecimenQuerySchema.LOCATION_SPECIMEN_LIST_TABLE_NAME); + CustomView defaultView = QueryService.get().getCustomView(getViewContext().getUser(), container, getViewContext().getUser(), querySchema.getName(), queryDef.getName(), null); + List columns = queryDef.getColumns(defaultView, table); + dr.setTable(table); + dr.setColumns(columns); + return dr; + } + + private static final String SELECTED_SPECIMENS_SESSION_ATTRIB_KEY = SpecimenController.class.getName() + "/SelectedSpecimens"; + + private Set getSelectionLsids() + { + // save the selected set of participantdataset lsids in the session; this is the only obvious way + // to let the user apply subsequent filters and switch back and forth between vial and specimen view + // without losing their original participant/visit selection. + Set lsids = null; + if (isPost()) + lsids = DataRegionSelection.getSelected(getViewContext(), true); + HttpSession session = getViewContext().getRequest().getSession(true); + Pair> selectionCache = (Pair>) session.getAttribute(SELECTED_SPECIMENS_SESSION_ATTRIB_KEY); + + boolean newFilter = (lsids != null && !lsids.isEmpty()); + boolean cachedFilter = selectionCache != null && getContainer().equals(selectionCache.getKey()); + if (!newFilter && !cachedFilter) + { + throw new RedirectException(getSpecimensURL()); + } + + if (newFilter) + selectionCache = new Pair<>(getContainer(), lsids); + + session.setAttribute(SELECTED_SPECIMENS_SESSION_ATTRIB_KEY, selectionCache); + return selectionCache.getValue(); + } + + @RequiresPermission(ReadPermission.class) + public class BeginAction extends SimpleRedirectAction + { + @Override + public ActionURL getRedirectURL(Object o) + { + return getSpecimensURL(); + } + } + + @RequiresPermission(ReadPermission.class) + public class OverviewAction extends SimpleViewAction + { + @Override + public ModelAndView getView(Object form, BindException errors) + { + if (null == StudyService.get().getStudy(getContainer())) + return new HtmlView("This folder does not contain a study."); + SpecimenSearchWebPart specimenSearch = new SpecimenSearchWebPart(true); + SpecimenWebPart specimenSummary = new SpecimenWebPart(true, StudyService.get().getStudy(getContainer())); + return new VBox(specimenSummary, specimenSearch); + } + + @Override + public void addNavTrail(NavTree root) + { + addBaseSpecimenNavTrail(root); + } + } + + public static class SpecimenWebPartForm + { + private String[] _grouping1; + private String[] _grouping2; + private String[] _columns; + + public String[] getGrouping1() + { + return _grouping1; + } + + public void setGrouping1(String[] grouping1) + { + _grouping1 = grouping1; + } + + public String[] getGrouping2() + { + return _grouping2; + } + + public void setGrouping2(String[] grouping2) + { + _grouping2 = grouping2; + } + + public String[] getColumns() + { + return _columns; + } + + public void setColumns(String[] columns) + { + _columns = columns; + } + } + + @RequiresPermission(ManageDisplaySettingsPermission.class) + public static class ManageSpecimenWebPartAction extends SimpleViewAction + { + @Override + public ModelAndView getView(SpecimenWebPartForm form, BindException errors) + { + RepositorySettings settings = SettingsManager.get().getRepositorySettings(getContainer()); + ArrayList groupings = settings.getSpecimenWebPartGroupings(); + form.setGrouping1(groupings.get(0)); + form.setGrouping2(groupings.get(1)); + form.setColumns(SpecimenRequestManager.get().getGroupedValueAllowedColumns()); + return new JspView<>("/org/labkey/specimen/view/manageSpecimenWebPart.jsp", form); + } + + @Override + public void addNavTrail(NavTree root) + { + setHelpTopic("manageSpecimens#group"); + urlProvider(StudyUrls.class).addManageStudyNavTrail(root, getContainer(), getUser()); + root.addChild("Configure Specimen Web Part"); + } + } + + @RequiresPermission(AdminPermission.class) + public class SaveSpecimenWebPartSettingsAction extends MutatingApiAction + { + @Override + public ApiResponse execute(SpecimenWebPartForm form, BindException errors) + { + ApiSimpleResponse response = new ApiSimpleResponse(); + Study study = getStudy(); + if (study != null) + { + Container container = getContainer(); + RepositorySettings settings = SettingsManager.get().getRepositorySettings(container); + ArrayList groupings = new ArrayList<>(2); + groupings.add(form.getGrouping1()); + groupings.add(form.getGrouping2()); + settings.setSpecimenWebPartGroupings(groupings); + SettingsManager.get().saveRepositorySettings(container, settings); + response.put("success", true); + return response; + } + else + throw new IllegalStateException("A study does not exist in this folder"); + } + } + + @RequiresSiteAdmin + public static class PivotAction extends SimpleViewAction + { + @Override + public ModelAndView getView(Object o, BindException errors) + { + return new JspView<>("/org/labkey/specimen/view/pivot.jsp"); + } + + @Override + public void addNavTrail(NavTree root) + { + } + } + + public static class PipelineForm extends PipelinePathForm + { + private String replaceOrMerge = "replace"; + + public String getReplaceOrMerge() + { + return replaceOrMerge; + } + + public void setReplaceOrMerge(String replaceOrMerge) + { + this.replaceOrMerge = replaceOrMerge; + } + + public boolean isMerge() + { + return "merge".equals(this.replaceOrMerge); + } + } + + public static void submitSpecimenBatch(Container c, User user, ActionURL url, File f, PipeRoot root, boolean merge) throws IOException + { + if (null == f || !f.exists() || !f.isFile()) + throw new NotFoundException(); + + SpecimenBatch batch = new SpecimenBatch(new ViewBackgroundInfo(c, user, url), f, root, merge); + batch.submit(); + } + + @RequiresPermission(AdminPermission.class) + public class SubmitSpecimenBatchImportAction extends FormHandlerAction + { + @Override + public void validateCommand(PipelineForm target, Errors errors) + { + } + + @Override + public boolean handlePost(PipelineForm form, BindException errors) throws Exception + { + Container c = getContainer(); + PipeRoot root = PipelineService.get().findPipelineRoot(c); + boolean first = true; + for (File f : form.getValidatedFiles(c)) + { + // Only possibly overwrite when the first archive is loaded: + boolean merge = !first || form.isMerge(); + submitSpecimenBatch(c, getUser(), getViewContext().getActionURL(), f, root, merge); + first = false; + } + return true; + } + + @Override + public ActionURL getSuccessURL(PipelineForm pipelineForm) + { + return urlProvider(PipelineStatusUrls.class).urlBegin(getContainer()); + } + } + + /** + * Legacy method hit via WGET/CURL to programmatically initiate a specimen import; no longer used by the UI, + * but this method should be kept around until we receive verification that the URL is no longer being hit + * programmatically. + */ + @RequiresPermission(AdminPermission.class) + public class SubmitSpecimenImport extends FormHandlerAction + { + @Override + public void validateCommand(PipelineForm target, Errors errors) + { + } + + @Override + public boolean handlePost(PipelineForm form, BindException errors) throws Exception + { + Container c = getContainer(); + String path = form.getPath(); + File f = null; + + PipeRoot root = PipelineService.get().findPipelineRoot(c); + if (path != null) + { + if (root != null) + f = root.resolvePath(path); + } + + submitSpecimenBatch(c, getUser(), getViewContext().getActionURL(), f, root, form.isMerge()); + return true; + } + + @Override + public ActionURL getSuccessURL(PipelineForm pipelineForm) + { + return urlProvider(PipelineStatusUrls.class).urlBegin(getContainer()); + } + } + + public static class ImportSpecimensBean + { + private final String _path; + private final List _archives; + private final List _errors; + private final Container _container; + private final String[] _files; + + private boolean noSpecimens = false; + private boolean _defaultMerge = false; + private boolean _isEditableSpecimens = false; + + public ImportSpecimensBean(Container container, List archives, + String path, String[] files, List errors) + { + _path = path; + _files = files; + _archives = archives; + _errors = errors; + _container = container; + } + + public List getArchives() + { + return _archives; + } + + public String getPath() + { + return _path; + } + + public String[] getFiles() + { + return _files; + } + + public List getErrors() + { + return _errors; + } + + public Container getContainer() + { + return _container; + } + + public boolean isNoSpecimens() + { + return noSpecimens; + } + + public void setNoSpecimens(boolean noSpecimens) + { + this.noSpecimens = noSpecimens; + } + + public boolean isDefaultMerge() + { + return _defaultMerge; + } + + public void setDefaultMerge(boolean defaultMerge) + { + _defaultMerge = defaultMerge; + } + + public boolean isEditableSpecimens() + { + return _isEditableSpecimens; + } + + public void setEditableSpecimens(boolean editableSpecimens) + { + _isEditableSpecimens = editableSpecimens; + } + } + + @RequiresPermission(AdminPermission.class) + public class ImportSpecimenDataAction extends SimpleViewAction + { + private String[] _filePaths = null; + + @Override + public ModelAndView getView(PipelineForm form, BindException bindErrors) + { + List dataFiles = form.getValidatedFiles(getContainer()); + List archives = new ArrayList<>(); + List errors = new ArrayList<>(); + _filePaths = form.getFile(); + for (File dataFile : dataFiles) + { + if (null == dataFile || !dataFile.exists() || !dataFile.isFile()) + { + throw new NotFoundException(); + } + + if (!dataFile.canRead()) + errors.add("Can't read data file: " + dataFile); + + SpecimenArchive archive = new SpecimenArchive(dataFile); + archives.add(archive); + } + + ImportSpecimensBean bean = new ImportSpecimensBean(getContainer(), archives, form.getPath(), form.getFile(), errors); + boolean isEmpty = SpecimenManagerNew.get().isSpecimensEmpty(getContainer(), getUser()); + if (isEmpty) + { + bean.setNoSpecimens(true); + } + else if (SettingsManager.get().getRepositorySettings(getStudyThrowIfNull().getContainer()).isSpecimenDataEditable()) + { + bean.setDefaultMerge(true); // Repository is editable; make Merge the default + bean.setEditableSpecimens(true); + } + + return new JspView<>("/org/labkey/specimen/view/importSpecimens.jsp", bean); + } + + @Override + public void addNavTrail(NavTree root) + { + String msg; + if (_filePaths.length == 1) + msg = _filePaths[0]; + else + msg = _filePaths.length + " specimen archives"; + root.addChild("Import Study Batch - " + msg); + } + } + + @RequiresPermission(AdminPermission.class) + public class GetSpecimenExcelAction extends SimpleViewAction + { + @Override + public ModelAndView getView(Object o, BindException errors) + { + List> defaultSpecimens = new ArrayList<>(); + SimpleSpecimenImporter importer = new SimpleSpecimenImporter(getContainer(), getUser(), + getStudyRedirectIfNull().getTimepointType(), StudyService.get().getSubjectNounSingular(getContainer())); + MapArrayExcelWriter xlWriter = new MapArrayExcelWriter(defaultSpecimens, importer.getSimpleSpecimenColumns()); + for (ExcelColumn col : xlWriter.getColumns()) + col.setCaption(importer.label(col.getName())); + + xlWriter.write(getViewContext().getResponse()); + + return null; + } + + @Override + public void addNavTrail(NavTree root) + { + } + } + + static class SpecimenEventForm + { + private String _id; + private Container _targetStudy; + + public String getId() + { + return _id; + } + + public void setId(String id) + { + _id = id; + } + + public Container getTargetStudy() + { + return _targetStudy; + } + + @SuppressWarnings("unused") + public void setTargetStudy(Container targetStudy) + { + _targetStudy = targetStudy; + } + } + + @SuppressWarnings("unused") // Referenced in SpecimenForeignKey + @RequiresPermission(ReadPermission.class) + public static class SpecimenEventsRedirectAction extends SimpleViewAction + { + @Override + public ModelAndView getView(SpecimenEventForm form, BindException errors) + { + if (form.getId() != null && form.getTargetStudy() != null) + { + Vial vial = SpecimenManagerNew.get().getVial(form.getTargetStudy(), getUser(), form.getId()); + if (vial != null) + { + ActionURL url = new ActionURL(SpecimenEventsAction.class, form.getTargetStudy()).addParameter("id", vial.getRowId()); + throw new RedirectException(url); + } + } + return new HtmlView("Unable to resolve the Specimen ID and target Study"); + } + + @Override + public void addNavTrail(NavTree root) + { + } + } + + public static class SpecimenEventAttachmentForm + { + private int _eventId; + private String _name; + + public int getEventId() + { + return _eventId; + } + + public void setEventId(int eventId) + { + _eventId = eventId; + } + + public String getName() + { + return _name; + } + + public void setName(String name) + { + _name = name; + } + } + + public static ActionURL getDownloadURL(SpecimenRequestEvent event, String name) + { + return new ActionURL(DownloadAction.class, event.getContainer()) + .addParameter("eventId", event.getRowId()) + .addParameter("name", name); + } + + @RequiresPermission(ReadPermission.class) + public static class DownloadAction extends BaseDownloadAction + { + @Override + public @Nullable Pair getAttachment(SpecimenEventAttachmentForm form) + { + SpecimenRequestEvent event = SpecimenRequestManager.get().getRequestEvent(getContainer(), form.getEventId()); + if (event == null) + throw new NotFoundException("Specimen event not found"); + + return new Pair<>(event, form.getName()); + } + } + + @RequiresPermission(ReadPermission.class) + public class AutoReportListAction extends SimpleViewAction + { + @Override + public ModelAndView getView(Object form, BindException errors) + { + return new JspView<>("/org/labkey/specimen/view/autoReportList.jsp", new ReportConfigurationBean(getViewContext())); + } + + @Override + public void addNavTrail(NavTree root) + { + setHelpTopic("exploreSpecimens"); + addBaseSpecimenNavTrail(root); + root.addChild("Specimen Reports"); + } + } + + @RequiresPermission(ReadPermission.class) + public class SpecimenRequestConfigRequiredAction extends SimpleViewAction + { + @Override + public ModelAndView getView(Object o, BindException errors) + { + return new JspView<>("/org/labkey/specimen/view/configurationRequired.jsp"); + } + + @Override + public void addNavTrail(NavTree root) + { + addRootNavTrail(root); + root.addChild("Unable to Request Specimens"); + } + } + + @RequiresPermission(ManageRequestSettingsPermission.class) + public class ConfigureRequestabilityRulesAction extends SimpleViewAction + { + @Override + public ModelAndView getView(Object o, BindException errors) + { + ensureSpecimenRequestsConfigured(false); + return new JspView<>("/org/labkey/specimen/view/configRequestabilityRules.jsp"); + } + + @Override + public void addNavTrail(NavTree root) + { + setHelpTopic("coordinateSpecimens#requestability"); + addManageStudyNavTrail(root); + root.addChild("Configure Requestability Rules"); + } + } + + @RequiresPermission(ReadPermission.class) + public class ViewRequestsAction extends SimpleViewAction + { + @Override + public ModelAndView getView(Object o, BindException errors) + { + requiresLogin(); + SpecimenRequestQueryView grid = SpecimenRequestQueryView.createView(getViewContext()); + grid.setExtraLinks(true); + grid.setShowCustomizeLink(false); + grid.setShowDetailsColumn(false); + if (getContainer().hasPermission(getUser(), RequestSpecimensPermission.class)) + { + ActionButton insertButton = new ActionButton(new ActionURL(ShowCreateSpecimenRequestAction.class, getContainer()), "Create New Request", ActionButton.Action.LINK); + grid.setButtons(Collections.singletonList(insertButton)); + } + else + { + grid.setButtons(Collections.emptyList()); + } + + JspView header = new JspView<>("/org/labkey/specimen/view/viewRequestsHeader.jsp", + new ViewRequestsHeaderBean(getViewContext(), grid)); + + return new VBox(header, grid); + } + + @Override + public void addNavTrail(NavTree root) + { + addSpecimenRequestsNavTrail(root); + } + } + + public static class ViewEventForm extends IdForm + { + private boolean _selected; + private boolean _vialView; + + public boolean isSelected() + { + return _selected; + } + + public void setSelected(boolean selected) + { + _selected = selected; + } + + public boolean isVialView() + { + return _vialView; + } + + public void setVialView(boolean vialView) + { + _vialView = vialView; + } + } + + @RequiresPermission(ReadPermission.class) + public class SpecimenEventsAction extends SimpleViewAction + { + private boolean _showingSelectedSpecimens; + + @Override + public ModelAndView getView(ViewEventForm viewEventForm, BindException errors) + { + Study study = getStudyThrowIfNull(); + _showingSelectedSpecimens = viewEventForm.isSelected(); + Vial vial = SpecimenManagerNew.get().getVial(getContainer(), getUser(), viewEventForm.getId()); + if (vial == null) + throw new NotFoundException("Specimen " + viewEventForm.getId() + " does not exist."); + + JspView summaryView = new JspView<>("/org/labkey/specimen/view/specimen.jsp", + new SpecimenEventBean(vial, viewEventForm.getReturnUrl())); + summaryView.setTitle("Vial Summary"); + + SpecimenEventQueryView vialHistoryView = SpecimenEventQueryView.createView(getViewContext(), vial); + vialHistoryView.setTitle("Vial History"); + + VBox vbox; + + if (SettingsManager.get().getRepositorySettings(getStudyRedirectIfNull().getContainer()).isEnableRequests()) + { + List requestIds = SpecimenRequestManager.get().getRequestIdsForSpecimen(vial); + SimpleFilter requestFilter; + WebPartView relevantRequests; + + if (!requestIds.isEmpty()) + { + requestFilter = new SimpleFilter(); + StringBuilder whereClause = new StringBuilder(); + for (int i = 0; i < requestIds.size(); i++) + { + if (i > 0) + whereClause.append(" OR "); + whereClause.append("RequestId = ?"); + } + requestFilter.addWhereClause(whereClause.toString(), requestIds.toArray()); + SpecimenRequestQueryView queryView = SpecimenRequestQueryView.createView(getViewContext(), requestFilter); + queryView.setExtraLinks(true); + relevantRequests = queryView; + } + else + relevantRequests = new JspView("/org/labkey/specimen/view/relevantRequests.jsp"); + relevantRequests.setTitle("Relevant Vial Requests"); + vbox = new VBox(summaryView, vialHistoryView, relevantRequests); + } + else + { + vbox = new VBox(summaryView, vialHistoryView); + } + + return vbox; + } + + @Override + public void addNavTrail(NavTree root) + { + addBaseSpecimenNavTrail(root); + if (_showingSelectedSpecimens) + { + root.addChild("Selected Specimens", SpecimenController.getSpecimensURL(getContainer(), true)); + } + root.addChild("Vial History"); + } + } + + private boolean isNullOrBlank(String toCheck) + { + return ((toCheck == null) || toCheck.equals("")); + } + + @RequiresPermission(ManageNotificationsPermission.class) + public class ManageNotificationsAction extends FormViewAction + { + @Override + public void validateCommand(RequestNotificationSettings form, Errors errors) + { + String replyTo = form.getReplyTo(); + if (replyTo == null || replyTo.length() == 0) + { + errors.reject(SpringActionController.ERROR_MSG, "Reply-to cannot be empty."); + } + else if (!RequestNotificationSettings.REPLY_TO_CURRENT_USER_VALUE.equals(replyTo)) + { + try + { + new ValidEmail(replyTo); + } + catch(ValidEmail.InvalidEmailException e) + { + errors.reject(SpringActionController.ERROR_MSG, replyTo + " is not a valid email address."); + } + } + + String subjectSuffix = form.getSubjectSuffix(); + if (subjectSuffix == null || subjectSuffix.length() == 0) + { + errors.reject(SpringActionController.ERROR_MSG, "Subject suffix cannot be empty."); + } + + try + { + form.getNewRequestNotifyAddresses(); + } + catch (ValidEmail.InvalidEmailException e) + { + errors.reject(SpringActionController.ERROR_MSG, e.getBadEmail() + " is not a valid email address."); + } + + try + { + form.getCCAddresses(); + } + catch (ValidEmail.InvalidEmailException e) + { + errors.reject(SpringActionController.ERROR_MSG, e.getBadEmail() + " is not a valid email address."); + } + } + + @Override + public ModelAndView getView(RequestNotificationSettings form, boolean reshow, BindException errors) + { + ensureSpecimenRequestsConfigured(false); + + // try to get the settings from the form, just in case this is a reshow: + RequestNotificationSettings settings = form; + if (settings == null || settings.getReplyTo() == null) + settings = SettingsManager.get().getRequestNotificationSettings(getContainer()); + + return new JspView<>("/org/labkey/specimen/view/manageNotifications.jsp", settings, errors); + } + + @Override + public boolean handlePost(RequestNotificationSettings settings, BindException errors) + { + ensureSpecimenRequestsConfigured(false); + + if (!settings.isNewRequestNotifyCheckbox()) + settings.setNewRequestNotify(null); + else + { + if (isNullOrBlank(settings.getNewRequestNotify())) + errors.reject(ERROR_MSG, "New request notify is blank and send email is checked"); + } + if (!settings.isCcCheckbox()) + settings.setCc(null); + else + { + if (isNullOrBlank(settings.getCc())) + errors.reject(ERROR_MSG, "Always CC is blank and send email is checked"); + } + if (errors.hasErrors()) + return false; + + SettingsManager.get().saveRequestNotificationSettings(getContainer(), settings); + return true; + } + + @Override + public ActionURL getSuccessURL(RequestNotificationSettings form) + { + return getManageStudyURL(); + } + + @Override + public void addNavTrail(NavTree root) + { + setHelpTopic("coordinateSpecimens#notify"); + addManageStudyNavTrail(root); + root.addChild("Manage Notifications"); + } + } + + @RequiresPermission(ManageDisplaySettingsPermission.class) + public class ManageDisplaySettingsAction extends FormViewAction + { + @Override + public void validateCommand(DisplaySettingsForm target, Errors errors) + { + } + + @Override + public ModelAndView getView(DisplaySettingsForm form, boolean reshow, BindException errors) + { + // try to get the settings from the form, just in case this is a reshow: + DisplaySettings settings = form.getBean(); + if (settings == null || settings.getLastVialEnum() == null) + settings = SettingsManager.get().getDisplaySettings(getContainer()); + + return new JspView<>("/org/labkey/specimen/view/manageDisplay.jsp", settings); + } + + @Override + public boolean handlePost(DisplaySettingsForm form, BindException errors) + { + DisplaySettings settings = form.getBean(); + SettingsManager.get().saveDisplaySettings(getContainer(), settings); + + return true; + } + + @Override + public ActionURL getSuccessURL(DisplaySettingsForm displaySettingsForm) + { + return getManageStudyURL(); + } + + @Override + public void addNavTrail(NavTree root) + { + setHelpTopic(new HelpTopic("specimenRequest#display")); + addManageStudyNavTrail(root); + root.addChild("Manage Specimen Display Settings"); + } + } + + public static class DisplaySettingsForm extends BeanViewForm + { + public DisplaySettingsForm() + { + super(DisplaySettings.class); + } + } + + public static class UpdateRequestabilityRulesForm implements HasViewContext + { + private ViewContext _viewContext; + private String[] _ruleType; + private String[] _ruleData; + private String[] _markType; + + @Override + public ViewContext getViewContext() + { + return _viewContext; + } + + @Override + public void setViewContext(ViewContext viewContext) + { + _viewContext = viewContext; + } + + public String[] getRuleType() + { + return _ruleType; + } + + @SuppressWarnings("unused") + public void setRuleType(String[] ruleType) + { + _ruleType = ruleType; + } + + public String[] getRuleData() + { + return _ruleData; + } + + @SuppressWarnings("unused") + public void setRuleData(String[] ruleData) + { + _ruleData = ruleData; + } + + public String[] getMarkType() + { + return _markType; + } + + public void setMarkType(String[] markType) + { + _markType = markType; + } + } + + @RequiresPermission(ManageRequestSettingsPermission.class) + public static class UpdateRequestabilityRulesAction extends MutatingApiAction + { + @Override + public ApiResponse execute(UpdateRequestabilityRulesForm form, BindException errors) + { + final List rules = new ArrayList<>(); + for (int i = 0; i < form.getRuleType().length; i++) + { + String typeName = form.getRuleType()[i]; + RequestabilityManager.RuleType type = RequestabilityManager.RuleType.valueOf(typeName); + String dataString = form.getRuleData()[i]; + rules.add(type.createRule(getContainer(), dataString)); + } + RequestabilityManager.getInstance().saveRules(getContainer(), getUser(), rules); + + return new ApiSimpleResponse(Collections.singletonMap("savedCount", rules.size())); + } + } + + @RequiresPermission(ManageStudyPermission.class) + public class ManageRepositorySettingsAction extends FormViewAction + { + @Override + public ModelAndView getView(ManageRepositorySettingsForm from, boolean reshow, BindException errors) + { + return new JspView<>("/org/labkey/specimen/view/manageRepositorySettings.jsp", SettingsManager.get().getRepositorySettings(getContainer())); + } + + @Override + public void validateCommand(ManageRepositorySettingsForm form, Errors errors) + { + } + + @Override + public boolean handlePost(ManageRepositorySettingsForm form, BindException errors) throws Exception + { + RepositorySettings settings = SettingsManager.get().getRepositorySettings(getContainer()); + settings.setSimple(form.isSimple()); + settings.setEnableRequests(!form.isSimple() && form.isEnableRequests()); + settings.setSpecimenDataEditable(!form.isSimple() && form.isSpecimenDataEditable()); + SettingsManager.get().saveRepositorySettings(getContainer(), settings); + + return true; + } + + @Override + public URLHelper getSuccessURL(ManageRepositorySettingsForm manageRepositorySettingsForm) + { + return getManageStudyURL(); + } + + @Override + public void addNavTrail(NavTree root) + { + setHelpTopic("specimenAdminTutorial"); + addManageStudyNavTrail(root); + root.addChild("Manage Repository Settings"); + } + } + + public static class ManageRepositorySettingsForm + { + private boolean _simple; + private boolean _enableRequests; + private boolean _specimenDataEditable; + + public boolean isSimple() + { + return _simple; + } + + public void setSimple(boolean simple) + { + _simple = simple; + } + + public boolean isEnableRequests() + { + return _enableRequests; + } + + public void setEnableRequests(boolean enableRequests) + { + _enableRequests = enableRequests; + } + + public boolean isSpecimenDataEditable() + { + return _specimenDataEditable; + } + + public void setSpecimenDataEditable(boolean specimenDataEditable) + { + _specimenDataEditable = specimenDataEditable; + } + } + + public static class DefaultRequirementsForm + { + private int _originatorActor; + private String _originatorDescription; + private int _providerActor; + private String _providerDescription; + private int _receiverActor; + private String _receiverDescription; + private int _generalActor; + private String _generalDescription; + private String _nextPage; + + public int getGeneralActor() + { + return _generalActor; + } + + @SuppressWarnings("unused") + public void setGeneralActor(int generalActor) + { + _generalActor = generalActor; + } + + public String getGeneralDescription() + { + return _generalDescription; + } + + @SuppressWarnings("unused") + public void setGeneralDescription(String generalDescription) + { + _generalDescription = generalDescription; + } + + public int getProviderActor() + { + return _providerActor; + } + + @SuppressWarnings("unused") + public void setProviderActor(int providerActor) + { + _providerActor = providerActor; + } + + public String getProviderDescription() + { + return _providerDescription; + } + + @SuppressWarnings("unused") + public void setProviderDescription(String providerDescription) + { + _providerDescription = providerDescription; + } + + public int getReceiverActor() + { + return _receiverActor; + } + + @SuppressWarnings("unused") + public void setReceiverActor(int receiverActor) + { + _receiverActor = receiverActor; + } + + public String getReceiverDescription() + { + return _receiverDescription; + } + + @SuppressWarnings("unused") + public void setReceiverDescription(String receiverDescription) + { + _receiverDescription = receiverDescription; + } + + public int getOriginatorActor() + { + return _originatorActor; + } + + @SuppressWarnings("unused") + public void setOriginatorActor(int originatorActor) + { + _originatorActor = originatorActor; + } + + public String getOriginatorDescription() + { + return _originatorDescription; + } + + @SuppressWarnings("unused") + public void setOriginatorDescription(String originatorDescription) + { + _originatorDescription = originatorDescription; + } + + public String getNextPage() + { + return _nextPage; + } + + @SuppressWarnings("unused") + public void setNextPage(String nextPage) + { + _nextPage = nextPage; + } + } + + @RequiresPermission(ManageRequestRequirementsPermission.class) + public class ManageDefaultReqsAction extends FormViewAction + { + @Override + public void validateCommand(DefaultRequirementsForm target, Errors errors) + { + } + + @Override + public ModelAndView getView(DefaultRequirementsForm defaultRequirementsForm, boolean reshow, BindException errors) + { + ensureSpecimenRequestsConfigured(true); + return new JspView<>("/org/labkey/specimen/view/manageDefaultReqs.jsp", + new ManageReqsBean(getUser(), getContainer())); + } + + @Override + public boolean handlePost(DefaultRequirementsForm form, BindException errors) + { + ensureSpecimenRequestsConfigured(true); + createDefaultRequirement(form.getOriginatorActor(), form.getOriginatorDescription(), SpecimenRequestRequirementType.ORIGINATING_SITE); + createDefaultRequirement(form.getProviderActor(), form.getProviderDescription(), SpecimenRequestRequirementType.PROVIDING_SITE); + createDefaultRequirement(form.getReceiverActor(), form.getReceiverDescription(), SpecimenRequestRequirementType.RECEIVING_SITE); + createDefaultRequirement(form.getGeneralActor(), form.getGeneralDescription(), SpecimenRequestRequirementType.NON_SITE_BASED); + return true; + } + + private void createDefaultRequirement(Integer actorId, String description, SpecimenRequestRequirementType type) + { + if (actorId != null && actorId.intValue() > 0 && description != null && description.length() > 0) + { + SpecimenRequestRequirement requirement = new SpecimenRequestRequirement(); + requirement.setContainer(getContainer()); + requirement.setActorId(actorId); + requirement.setDescription(description); + requirement.setRequestId(-1); + SpecimenRequestRequirementProvider.get().createDefaultRequirement(getUser(), requirement, type); + } + } + + @Override + public ActionURL getSuccessURL(DefaultRequirementsForm form) + { + if (form.getNextPage() != null && form.getNextPage().length() > 0) + return new ActionURL(form.getNextPage()); + else + return getManageStudyURL(); + } + + @Override + public void addNavTrail(NavTree root) + { + setHelpTopic("coordinateSpecimens#requirements"); + addManageStudyNavTrail(root); + root.addChild("Manage Default Requirements"); + } + } + + @RequiresPermission(ManageRequestRequirementsPermission.class) + public class DeleteDefaultRequirementAction extends FormHandlerAction + { + @Override + public void validateCommand(IdForm target, Errors errors) + { + } + + @Override + public boolean handlePost(IdForm form, BindException errors) throws Exception + { + SpecimenRequestRequirement requirement = + SpecimenRequestRequirementProvider.get().getRequirement(getContainer(), form.getId()); + // we should only be deleting default requirements (those without an associated request): + if (requirement != null && requirement.getRequestId() == -1) + { + SpecimenRequestManager.get().deleteRequestRequirement(getUser(), requirement, false); + return true; + } + + return false; + } + + @Override + public ActionURL getSuccessURL(IdForm requirementForm) + { + return new ActionURL(ManageDefaultReqsAction.class, getContainer()); + } + } + + public static class CompleteSpecimenForm + { + private String _prefix; + + public String getPrefix() + { + return _prefix; + } + + public void setPrefix(String prefix) + { + _prefix = prefix; + } + } + + @RequiresPermission(AdminPermission.class) + public class CompleteSpecimenAction extends ReadOnlyApiAction + { + @Override + public ApiResponse execute(CompleteSpecimenForm form, BindException errors) + { + ApiSimpleResponse response = new ApiSimpleResponse(); + + Study study = getStudy(); + if (study == null) + throw new NotFoundException("No study exists in this folder."); + + List completions = new ArrayList<>(); + for (AjaxCompletion completion : getAjaxCompletions(study)) + completions.add(completion.toJSON()); + + response.put("completions", completions); + return response; + } + } + + private List getAjaxCompletions(Study study) + { + List completions = new ArrayList<>(); + String allString = "All " + PageFlowUtil.filter(StudyService.get().getSubjectNounPlural(study.getContainer())) + " (Large Report)"; + + completions.add(new AjaxCompletion(allString, allString)); + + for (String ptid : StudyService.get().getParticipantIds(study, getViewContext().getUser())) + completions.add(new AjaxCompletion(ptid, ptid)); + + return completions; + } + public static List getSpecimensFromRowIds(long[] requestedSampleIds, Container container, User user) + { + List requestedVials = null; + + if (requestedSampleIds != null) + { + List vials = new ArrayList<>(); + for (long requestedSampleId : requestedSampleIds) + { + Vial current = SpecimenManagerNew.get().getVial(container, user, requestedSampleId); + if (current != null) + vials.add(current); + } + requestedVials = vials; + } + + return requestedVials; + } + + private static long[] toLongArray(Collection intStrings) + { + if (intStrings == null) + return null; + long[] converted = new long[intStrings.size()]; + int index = 0; + for (String intString : intStrings) + converted[index++] = Long.parseLong(intString); + return converted; + } + + public static List getSpecimensFromRowIds(Collection ids, Container container, User user) + { + return getSpecimensFromRowIds(toLongArray(ids), container, user); + } + + public List getSpecimensFromPost(boolean fromGroupedView, boolean onlyAvailable) + { + Set formValues = null; + if ("POST".equalsIgnoreCase(getViewContext().getRequest().getMethod())) + formValues = DataRegionSelection.getSelected(getViewContext(), true); + + if (formValues == null || formValues.isEmpty()) + return null; + + List selectedVials; + if (fromGroupedView) + { + Map> keyToVialMap = + SpecimenManagerNew.get().getVialsForSpecimenHashes(getContainer(), getUser(), formValues, onlyAvailable); + List vials = new ArrayList<>(); + for (List vialList : keyToVialMap.values()) + vials.addAll(vialList); + selectedVials = new ArrayList<>(vials); + } + else + selectedVials = getSpecimensFromRowIds(formValues, getContainer(), getUser()); + return selectedVials; + } + + public static class CreateSpecimenRequestForm extends ReturnUrlForm implements HiddenFormInputGenerator + { + public enum PARAMS + { + returnUrl, + extendedRequestUrl, + ignoreReturnUrl + } + + private String[] _inputs; + private int _destinationLocation; + private long[] _specimenRowIds; + private boolean[] _required; + private boolean _fromGroupedView; + private Integer _preferredLocation; + private boolean _ignoreReturnUrl; + private boolean _extendedRequestUrl; + private String[] _specimenIds; + + @Override + public String getHiddenFormInputs(ViewContext ctx) + { + StringBuilder builder = new StringBuilder(); + if (_inputs != null) + { + for (String input : _inputs) + builder.append("\n"); + } + if (_destinationLocation != 0) + builder.append("\n"); + if (getReturnUrl() != null) + builder.append("\n"); + if (_specimenRowIds != null) + { + for (long specimenId : _specimenRowIds) + builder.append("\n"); + } + else + { + String dataRegionSelectionKey = ctx.getRequest().getParameter(DataRegionSelection.DATA_REGION_SELECTION_KEY); + if (dataRegionSelectionKey != null) + { + builder.append("\n"); + Set specimenFormValues = DataRegionSelection.getSelected(ctx, false); + for (String formValue : specimenFormValues) + { + builder.append("\n"); + } + } + } + + if (_required != null) + { + for (boolean required : _required) + builder.append("\n"); + } + + if (_specimenIds != null) + { + for (String specimenId : _specimenIds) + builder.append("\n"); + } + + builder.append("\n"); + return builder.toString(); + } + + public long[] getSpecimenRowIds() + { + return _specimenRowIds; + } + + public void setSpecimenRowIds(long[] specimenRowIds) + { + _specimenRowIds = specimenRowIds; + } + + public int getDestinationLocation() + { + return _destinationLocation; + } + + public void setDestinationLocation(int destinationLocation) + { + _destinationLocation = destinationLocation; + } + + public String[] getInputs() + { + return _inputs; + } + + public void setInputs(String[] inputs) + { + _inputs = inputs; + } + + public boolean[] getRequired() + { + return _required; + } + + public void setRequired(boolean[] required) + { + _required = required; + } + + public boolean isFromGroupedView() + { + return _fromGroupedView; + } + + public void setFromGroupedView(boolean fromGroupedView) + { + _fromGroupedView = fromGroupedView; + } + + public Integer getPreferredLocation() + { + return _preferredLocation; + } + + public void setPreferredLocation(Integer preferredLocation) + { + _preferredLocation = preferredLocation; + } + + public boolean isIgnoreReturnUrl() + { + return _ignoreReturnUrl; + } + + public void setIgnoreReturnUrl(boolean ignoreReturnUrl) + { + _ignoreReturnUrl = ignoreReturnUrl; + } + + public boolean isExtendedRequestUrl() + { + return _extendedRequestUrl; + } + + public void setExtendedRequestUrl(boolean extendedRequestUrl) + { + _extendedRequestUrl = extendedRequestUrl; + } + + public String[] getSpecimenIds() + { + return _specimenIds; + } + + public void setSpecimenIds(String[] specimenIds) + { + _specimenIds = specimenIds; + } + + public RequestedSpecimens getSelectedSpecimens(ViewContext ctx) throws AmbiguousLocationException + { + Container container = ctx.getContainer(); + User user = ctx.getUser(); + HttpServletRequest request = ctx.getRequest(); + + // first check for explicitly listed specimen row ids (this is the case when posting the final + // specimen request form): + List requestedSpecimens = getSpecimensFromRowIds(getSpecimenRowIds(), container, user); + if (requestedSpecimens != null && requestedSpecimens.size() > 0) + return new RequestedSpecimens(requestedSpecimens); + + Set ids; + if ("post".equalsIgnoreCase(request.getMethod()) && + (request.getParameter(DataRegionSelection.DATA_REGION_SELECTION_KEY) != null)) + { + ids = DataRegionSelection.getSelected(ctx, null, true); + if (isFromGroupedView()) + return SpecimenRequestManager.get().getRequestableBySpecimenHash(container, user, ids, getPreferredLocation()); + else + return getRequestableByVialRowIds(ids, container, user); + } + else if (_specimenIds != null && _specimenIds.length > 0) + { + ids = new HashSet<>(); + Collections.addAll(ids, _specimenIds); + if (isFromGroupedView()) + return SpecimenRequestManager.get().getRequestableBySpecimenHash(container, user, ids, getPreferredLocation()); + else + return getRequestableByVialGlobalUniqueIds(ids, container, user); + } + else + return null; + } + + private RequestedSpecimens getRequestableByVialRowIds(Set rowIds, Container container, User user) + { + Set ids = new HashSet<>(); + Arrays.stream(toLongArray(rowIds)).forEach(ids::add); + List requestedSpecimens = SpecimenManagerNew.get().getRequestableVials(container, user, ids); + return new RequestedSpecimens(requestedSpecimens); + } + + private RequestedSpecimens getRequestableByVialGlobalUniqueIds(Set globalUniqueIds, Container container, User user) + { + List requestedVials = null; + + if (globalUniqueIds != null) + { + List vials = new ArrayList<>(); + for (String globalUniqueId : globalUniqueIds) + { + Vial match = SpecimenManagerNew.get().getVial(container, user, globalUniqueId); + if (match != null) + vials.add(match); + } + requestedVials = new ArrayList<>(vials); + } + return new RequestedSpecimens(requestedVials); + } + } + + public abstract static class SpecimensViewBean + { + protected SpecimenQueryView _specimenQueryView; + protected List _vials; + + public SpecimensViewBean(ViewContext context, List vials, boolean showHistoryLinks, + boolean showRecordSelectors, boolean disableLowVialIndicators, boolean restrictRecordSelectors) + { + _vials = vials; + if (vials != null && vials.size() > 0) + { + _specimenQueryView = SpecimenQueryView.createView(context, vials, SpecimenQueryView.ViewType.VIALS); + _specimenQueryView.setShowHistoryLinks(showHistoryLinks); + _specimenQueryView.setShowRecordSelectors(showRecordSelectors); + _specimenQueryView.setDisableLowVialIndicators(disableLowVialIndicators); + _specimenQueryView.setRestrictRecordSelectors(restrictRecordSelectors); + } + } + + public SpecimenQueryView getSpecimenQueryView() + { + return _specimenQueryView; + } + + public List getVials() + { + return _vials; + } + } + + public static class NewRequestBean extends SpecimensViewBean + { + private final Container _container; + private final SpecimenRequestManager.SpecimenRequestInput[] _inputs; + private final String[] _inputValues; + private final int _selectedSite; + private final BindException _errors; + private final ActionURL _returnUrl; + + public NewRequestBean(ViewContext context, RequestedSpecimens requestedSpecimens, CreateSpecimenRequestForm form, BindException errors) throws SQLException + { + super(context, requestedSpecimens != null ? requestedSpecimens.getVials() : null, false, false, false, false); + _errors = errors; + _inputs = SpecimenRequestManager.get().getNewSpecimenRequestInputs(context.getContainer()); + _selectedSite = form.getDestinationLocation(); + _inputValues = form.getInputs(); + _container = context.getContainer(); + _returnUrl = form.getReturnActionURL(); + } + + public SpecimenRequestManager.SpecimenRequestInput[] getInputs() + { + return _inputs; + } + + public String getValue(int inputIndex) throws ValidationException + { + if (_inputValues != null && inputIndex < _inputValues.length && _inputValues[inputIndex] != null) + return _inputValues[inputIndex]; + if (_inputs[inputIndex].isRememberSiteValue() && _selectedSite > 0) + return _inputs[inputIndex].getDefaultSiteValues(_container).get(_selectedSite); + return ""; + } + + public int getSelectedSite() + { + return _selectedSite; + } + + public BindException getErrors() + { + return _errors; + } + + public ActionURL getReturnUrl() + { + return _returnUrl; + } + } + + @RequiresPermission(RequestSpecimensPermission.class) + public class HandleCreateSpecimenRequestAction extends FormViewAction + { + private SpecimenRequest _specimenRequest; + + @Override + public ModelAndView getView(CreateSpecimenRequestForm form, boolean reshow, BindException errors) throws Exception + { + return getCreateSpecimenRequestView(form, errors); + } + + @Override + public void addNavTrail(NavTree root) + { + addSpecimenRequestsNavTrail(root); + root.addChild("New Specimen Request"); + } + + @Override + public void validateCommand(CreateSpecimenRequestForm form, Errors errors) + { + boolean missingRequiredInput = false; + if (form.getDestinationLocation() <= 0) + missingRequiredInput = true; + + for (int i = 0; i < form.getInputs().length && !missingRequiredInput; i++) + { + if (form.getRequired()[i] && (form.getInputs()[i] == null || form.getInputs()[i].length() == 0)) + missingRequiredInput = true; + } + + if (missingRequiredInput) + errors.reject(null, "Please provide all required input."); + } + + @Override + public boolean handlePost(CreateSpecimenRequestForm form, BindException errors) throws Exception + { + ensureSpecimenRequestsConfigured(true); + + String[] inputs = form.getInputs(); + long[] specimenIds = form.getSpecimenRowIds(); + StringBuilder comments = new StringBuilder(); + SpecimenRequestManager.SpecimenRequestInput[] expectedInputs = + SpecimenRequestManager.get().getNewSpecimenRequestInputs(getContainer()); + if (inputs.length != expectedInputs.length) + throw new IllegalStateException("Expected a form element for each input."); + + for (int i = 0; i < expectedInputs.length; i++) + { + SpecimenRequestManager.SpecimenRequestInput expectedInput = expectedInputs[i]; + if (form.getDestinationLocation() != 0 && expectedInput.isRememberSiteValue()) + expectedInput.setDefaultSiteValue(getContainer(), form.getDestinationLocation(), inputs[i]); + if (i > 0) + comments.append("\n\n"); + comments.append(expectedInput.getTitle()).append(":\n"); + if (inputs[i] != null && inputs[i].length() > 0) + comments.append(inputs[i]); + else + comments.append("[Not provided]"); + } + + _specimenRequest = new SpecimenRequest(); + _specimenRequest.setComments(comments.toString()); + _specimenRequest.setContainer(getContainer()); + Timestamp ts = new Timestamp(System.currentTimeMillis()); + _specimenRequest.setCreated(ts); + _specimenRequest.setModified(ts); + _specimenRequest.setEntityId(GUID.makeGUID()); + Integer defaultSiteId = SpecimenService.get().getRequestCustomizer().getDefaultDestinationSiteId(); + // Default takes precedence if set + if (defaultSiteId != null) + { + _specimenRequest.setDestinationSiteId(defaultSiteId); + } + else if (form.getDestinationLocation() > 0) + { + _specimenRequest.setDestinationSiteId(form.getDestinationLocation()); + } + _specimenRequest.setStatusId(SpecimenRequestManager.get().getInitialRequestStatus(getContainer(), getUser(), false).getRowId()); + + DbScope scope = SpecimenSchema.get().getScope(); + try (DbScope.Transaction transaction = scope.ensureTransaction()) + { + if (!LocationManager.get().isSiteValidRequestingLocation(getContainer(), _specimenRequest.getDestinationSiteId())) + { + errors.reject(ERROR_MSG, "The requesting location is not valid."); + return false; + } + + User user = getUser(); + Container container = getContainer(); + _specimenRequest = SpecimenRequestManager.get().createRequest(getUser(), _specimenRequest, true); + List vials; + if (specimenIds != null && specimenIds.length > 0) + { + vials = new ArrayList<>(); + for (long specimenId : specimenIds) + { + Vial vial = SpecimenManagerNew.get().getVial(container, user, specimenId); + if (vial != null) + { + boolean isAvailable = vial.isAvailable(); + if (!isAvailable) + { + errors.reject(null, RequestabilityManager.makeSpecimenUnavailableMessage(vial, "This specimen has been removed from the list below.")); + } + else + vials.add(vial); + } + else + { + ExceptionUtil.logExceptionToMothership(getViewContext().getRequest(), + new IllegalStateException("Specimen ID " + specimenId + " was not found in container " + container.getId())); + } + } + if (errors.getErrorCount() == 0) + { + try + { + SpecimenRequestManager.get().createRequestSpecimenMapping(getUser(), _specimenRequest, vials, true, true); + } + catch (RequestabilityManager.InvalidRuleException e) + { + errors.reject(ERROR_MSG, "The request could not be created because a requestability rule is configured incorrectly. " + + "Please report this problem to an administrator. Error details: " + e.getMessage()); + return false; + } + catch (SpecimenRequestException e) + { + errors.reject(ERROR_MSG, "A vial that was available for request has become unavailable."); + return false; + } + } + else + { + long[] validSpecimenIds = new long[vials.size()]; + int index = 0; + for (Vial vial : vials) + validSpecimenIds[index++] = vial.getRowId(); + form.setSpecimenRowIds(validSpecimenIds); + return false; + } + } + transaction.commit(); + } + + if (!SettingsManager.get().isSpecimenShoppingCartEnabled(getContainer())) + { + try + { + sendNewRequestNotifications(_specimenRequest, errors); + } + catch (ConfigurationException | IOException e) + { + errors.reject(ERROR_MSG, e.getMessage()); + } + } + + if (errors.hasErrors()) + return false; + + Study study = getStudy(); + if (null == study) + throw new NotFoundException("No study exists in this folder."); + StudyInternalService.get().setLastSpecimenRequest(study, _specimenRequest.getRowId()); + return true; + } + + @Override + public ActionURL getSuccessURL(CreateSpecimenRequestForm createSpecimenRequestForm) + { + ActionURL modifiedReturnURL = null; + if (createSpecimenRequestForm.isExtendedRequestUrl()) + { + return getExtendedRequestURL(_specimenRequest.getRowId(), null); + } + if (createSpecimenRequestForm.getReturnUrl() != null) + { + modifiedReturnURL = createSpecimenRequestForm.getReturnActionURL(); + } + if (modifiedReturnURL != null && !createSpecimenRequestForm.isIgnoreReturnUrl()) + return modifiedReturnURL; + else + return getManageRequestURL(_specimenRequest.getRowId(), modifiedReturnURL); + } + } + + private ModelAndView getCreateSpecimenRequestView(CreateSpecimenRequestForm form, BindException errors) throws SQLException + { + ensureSpecimenRequestsConfigured(true); + + RequestedSpecimens requested; + + try + { + requested = form.getSelectedSpecimens(getViewContext()); + } + catch (AmbiguousLocationException e) + { + // Even though this method (getCreateSpecimenRequestView) is used from multiple places, only HandleCreateSpecimenRequestAction + // receives a post; therefore, it's safe to say that the selectSpecimenProvider.jsp form should always post to + // HandleCreateSpecimenRequestAction. + return new JspView<>("/org/labkey/specimen/view/selectSpecimenProvider.jsp", + new SelectSpecimenProviderBean(form, e.getPossibleLocations(), new ActionURL(ShowCreateSpecimenRequestAction.class, getContainer())), errors); + } + + return new JspView<>("/org/labkey/specimen/view/requestSpecimens.jsp", + new NewRequestBean(getViewContext(), requested, form, errors)); + } + + @RequiresPermission(RequestSpecimensPermission.class) + public class ShowCreateSpecimenRequestAction extends SimpleViewAction + { + @Override + public ModelAndView getView(CreateSpecimenRequestForm form, BindException errors) throws Exception + { + return getCreateSpecimenRequestView(form, errors); + } + + @Override + public void addNavTrail(NavTree root) + { + setHelpTopic("specimenShopping"); + addSpecimenRequestsNavTrail(root); + root.addChild("New Specimen Request"); + } + } + + @RequiresPermission(RequestSpecimensPermission.class) + public class ShowAPICreateSpecimenRequestAction extends SimpleViewAction + { + @Override + public ModelAndView getView(CreateSpecimenRequestForm form, BindException errors) throws Exception + { + return getCreateSpecimenRequestView(form, errors); + } + + @Override + public void addNavTrail(NavTree root) + { + addSpecimenRequestsNavTrail(root); + root.addChild("New Specimen Request"); + } + } + + private ActionURL getManageRequestURL(int requestID, @Nullable ActionURL returnUrl) + { + return getManageRequestURL(getContainer(), requestID, returnUrl); + } + + public static ActionURL getManageRequestURL(Container c, int requestID, @Nullable ActionURL returnUrl) + { + ActionURL url = new ActionURL(ManageRequestAction.class, c); + url.addParameter(IdForm.PARAMS.id, Integer.toString(requestID)); + if (returnUrl != null) + url.addReturnURL(returnUrl); + return url; + } + + private ActionURL getExtendedRequestURL(int requestID, String returnUrl) + { + ActionURL url = new ActionURL(ExtendedSpecimenRequestAction.class, getContainer()); + url.addParameter(IdForm.PARAMS.id, Integer.toString(requestID)); +// if (returnUrl != null) +// url.addReturnURL(returnUrl); + return url; + } + + @RequiresPermission(RequestSpecimensPermission.class) + public class ExtendedSpecimenRequestAction extends SimpleViewAction + { + @Override + public ModelAndView getView(CreateSpecimenRequestForm form, BindException errors) + { + VBox vbox = new VBox(); + + ExtendedSpecimenRequestView view = SpecimenManager.get().getExtendedSpecimenRequestView(getViewContext()); + if (view != null && view.isActive()) + { + HtmlView requestView = new HtmlView(view.getBody()); + vbox.addView(requestView); + } + else + { + vbox.addView(new HtmlView("An extended specimen request view has not been provided for this folder.")); + } + return vbox; + } + + @Override + public void addNavTrail(NavTree root) + { + addSpecimenRequestsNavTrail(root); + root.addChild("Extended Specimen Request"); + } + } + + public static class BulkEditForm + { + private String _newLabel; + private String _newId; + private String _nextPage; + private String _order; + private int[] _ids; + private String[] _labels; + + public String getNewLabel() + { + return _newLabel; + } + + public void setNewLabel(String newLabel) + { + _newLabel = newLabel; + } + + public String getNextPage() + { + return _nextPage; + } + + public void setNextPage(String nextPage) + { + _nextPage = nextPage; + } + + public String getOrder() + { + return _order; + } + + public void setOrder(String order) + { + _order = order; + } + + public String[] getLabels() + { + return _labels; + } + + public void setLabels(String[] labels) + { + _labels = labels; + } + + public int[] getIds() + { + return _ids; + } + + public void setIds(int[] ids) + { + _ids = ids; + } + + public String getNewId() + { + return _newId; + } + + public void setNewId(String newId) + { + _newId = newId; + } + } + + private abstract class DisplayManagementSubpageAction
extends FormViewAction + { + private final String _jsp; + private final String _title; + private final String _helpTopic; + + public DisplayManagementSubpageAction(String jsp, String title, String helpTopic) + { + _jsp = jsp; + _title = title; + _helpTopic = helpTopic; + } + + @Override + public void validateCommand(Form target, Errors errors) + { + } + + @Override + public ModelAndView getView(Form form, boolean reshow, BindException errors) + { + ensureSpecimenRequestsConfigured(false); + return getJspView(getStudyRedirectIfNull()); + } + + protected abstract JspView getJspView(Study study); + + @Override + public void addNavTrail(NavTree root) + { + setHelpTopic(new HelpTopic(_helpTopic)); + addManageStudyNavTrail(root); + root.addChild(_title); + } + } + + public static class ActorEditForm extends BulkEditForm + { + boolean _newPerSite; + + public boolean isNewPerSite() + { + return _newPerSite; + } + + @SuppressWarnings("unused") + public void setNewPerSite(boolean newPerSite) + { + _newPerSite = newPerSite; + } + } + + private Map getIdToRequestActorMap(Container container) + { + SpecimenRequestActor[] actors = SpecimenRequestRequirementProvider.get().getActors(container); + Map idToStatus = new HashMap<>(); + for (SpecimenRequestActor actor : actors) + idToStatus.put(actor.getRowId(), actor); + return idToStatus; + } + + @RequiresPermission(ManageSpecimenActorsPermission.class) + public class ManageActorsAction extends DisplayManagementSubpageAction + { + public ManageActorsAction() + { + super("manageActors", "Manage Specimen Request Actors", "coordinateSpecimens#actor"); + } + + @Override + protected JspView getJspView(Study study) + { + return new JspView<>("/org/labkey/specimen/view/manageActors.jsp", study); + } + + @Override + public boolean handlePost(ActorEditForm form, BindException errors) + { + ensureSpecimenRequestsConfigured(false); + + int[] rowIds = form.getIds(); + String[] labels = form.getLabels(); + if (labels != null) + { + for (String label : labels) + { + if (label == null || label.length() == 0) + errors.reject(ERROR_MSG, "Actor name cannot be empty."); + } + } + if (!errors.hasErrors()) + { + if (rowIds != null && rowIds.length > 0) + { + // get a map of id to actor objects before starting our updates; this prevents us from + // blowing then repopulating the cache with each update: + Map idToActor = getIdToRequestActorMap(getContainer()); + for (int i = 0; i < rowIds.length; i++) + { + int rowId = rowIds[i]; + String label = labels[i]; + SpecimenRequestActor actor = idToActor.get(rowId); + if (actor != null && !nullSafeEqual(label, actor.getLabel())) + { + actor = actor.createMutable(); + actor.setLabel(label); + actor.update(getUser()); + } + } + } + + if (form.getNewLabel() != null && form.getNewLabel().length() > 0) + { + SpecimenRequestActor actor = new SpecimenRequestActor(); + actor.setLabel(form.getNewLabel()); + SpecimenRequestActor[] actors = SpecimenRequestRequirementProvider.get().getActors(getContainer()); + actor.setSortOrder(actors.length); + actor.setContainer(getContainer()); + actor.setPerSite(form.isNewPerSite()); + actor.create(getUser()); + } + } + return !errors.hasErrors(); + } + + @Override + public ActionURL getSuccessURL(ActorEditForm form) + { + if (form.getNextPage() != null && form.getNextPage().length() > 0) + return new ActionURL(form.getNextPage()); + else + return getManageStudyURL(); + } + } + + @RequiresPermission(ManageSpecimenActorsPermission.class) + public class ManageActorOrderAction extends DisplayManagementSubpageAction + { + public ManageActorOrderAction() + { + super("manageActorOrder", "Manage Actor Order", "specimenRequest"); + } + + @Override + protected JspView getJspView(Study study) + { + return new JspView<>("/org/labkey/specimen/view/manageActorOrder.jsp", study); + } + + @Override + public boolean handlePost(BulkEditForm form, BindException errors) + { + ensureSpecimenRequestsConfigured(false); + + String order = form.getOrder(); + if (order != null && order.length() > 0) + { + String[] rowIds = order.split(","); + // get a map of id to actor objects before starting our updates; this prevents us from + // blowing then repopulating the cache with each update: + Map idToActor = getIdToRequestActorMap(getContainer()); + for (int i = 0; i < rowIds.length; i++) + { + int rowId = Integer.parseInt(rowIds[i]); + SpecimenRequestActor actor = idToActor.get(rowId); + if (actor != null && actor.getSortOrder() != i) + { + actor = actor.createMutable(); + actor.setSortOrder(i); + actor.update(getUser()); + } + } + } + return true; + } + + @Override + public ActionURL getSuccessURL(BulkEditForm bulkEditForm) + { + return new ActionURL(ManageActorsAction.class, getContainer()); + } + } + + @RequiresPermission(ManageRequestStatusesPermission.class) + public class DeleteActorAction extends FormHandlerAction + { + @Override + public void validateCommand(IdForm target, Errors errors) + { + } + + @Override + public boolean handlePost(IdForm form, BindException errors) + { + SpecimenRequestActor actor = SpecimenRequestRequirementProvider.get().getActor(getContainer(), form.getId()); + if (actor != null) + actor.delete(); + + return true; + } + + @Override + public ActionURL getSuccessURL(IdForm idForm) + { + return new ActionURL(ManageActorsAction.class, getContainer()); + } + } + + private void updateRequestStatusOrder(Container container, int[] rowIds, boolean fixedRowIncluded) + { + // get a map of id to status objects before starting our updates; this prevents us from + // blowing then repopulating the cache with each update: + Map idToStatus = getIdToRequestStatusMap(container); + for (int i = 0; i < rowIds.length; i++) + { + int rowId = rowIds[i]; + int statusOrder = fixedRowIncluded ? i : i + 1; // One caller doesn't have the first (fixed) status + SpecimenRequestStatus status = idToStatus.get(rowId); + if (status != null && !status.isSystemStatus() && status.getSortOrder() != statusOrder) + { + status = status.createMutable(); + status.setSortOrder(statusOrder); + SpecimenRequestManager.get().updateRequestStatus(getUser(), status); + } + } + } + + private Map getIdToRequestStatusMap(Container container) + { + List statuses = SpecimenRequestManager.get().getRequestStatuses(container, getUser()); + Map idToStatus = new HashMap<>(); + for (SpecimenRequestStatus status : statuses) + idToStatus.put(status.getRowId(), status); + return idToStatus; + } + + public static class StatusEditForm extends BulkEditForm + { + private int[] _finalStateIds = new int[0]; + private int[] _specimensLockedIds = new int[0]; + private boolean _newSpecimensLocked; + private boolean _newFinalState; + private boolean _useShoppingCart; + + public int[] getFinalStateIds() + { + return _finalStateIds; + } + + public void setFinalStateIds(int[] finalStateIds) + { + _finalStateIds = finalStateIds; + } + + public boolean isNewFinalState() + { + return _newFinalState; + } + + public void setNewFinalState(boolean newFinalState) + { + _newFinalState = newFinalState; + } + + public boolean isNewSpecimensLocked() + { + return _newSpecimensLocked; + } + + public void setNewSpecimensLocked(boolean newSpecimensLocked) + { + _newSpecimensLocked = newSpecimensLocked; + } + + public int[] getSpecimensLockedIds() + { + return _specimensLockedIds; + } + + public void setSpecimensLockedIds(int[] specimensLockedIds) + { + _specimensLockedIds = specimensLockedIds; + } + + public boolean isUseShoppingCart() + { + return _useShoppingCart; + } + + public void setUseShoppingCart(boolean useShoppingCart) + { + _useShoppingCart = useShoppingCart; + } + } + + @RequiresPermission(ManageRequestStatusesPermission.class) + public class ManageStatusesAction extends DisplayManagementSubpageAction + { + public ManageStatusesAction() + { + super("manageStatuses", "Manage Specimen Request Statuses", "specimenRequest#status"); + } + + @Override + protected JspView getJspView(Study study) + { + return new JspView<>("/org/labkey/specimen/view/manageStatuses.jsp", study); + } + + @Override + public boolean handlePost(StatusEditForm form, BindException errors) + { + ensureSpecimenRequestsConfigured(false); + + int[] rowIds = form.getIds(); + String[] labels = form.getLabels(); + if (rowIds != null && rowIds.length > 0) + { + // get a map of id to status objects before starting our updates; this prevents us from + // blowing then repopulating the cache with each update: + Map idToStatus = getIdToRequestStatusMap(getContainer()); + Set finalStates = new HashSet<>(form.getFinalStateIds().length); + for (int id : form.getFinalStateIds()) + finalStates.add(id); + Set lockedSpecimenStates = new HashSet<>(form.getSpecimensLockedIds().length); + for (int id : form.getSpecimensLockedIds()) + lockedSpecimenStates.add(id); + + for (int i = 0; i < rowIds.length; i++) + { + int rowId = rowIds[i]; + SpecimenRequestStatus status = idToStatus.get(rowId); + if (status != null && !status.isSystemStatus()) + { + String label = labels[i]; + boolean isFinalState = finalStates.contains(rowId); + boolean specimensLocked = lockedSpecimenStates.contains(rowId); + if (!nullSafeEqual(label, status.getLabel()) || + isFinalState != status.isFinalState() || + specimensLocked != status.isSpecimensLocked()) + { + status = status.createMutable(); + status.setLabel(label); + status.setFinalState(isFinalState); + status.setSpecimensLocked(specimensLocked); + SpecimenRequestManager.get().updateRequestStatus(getUser(), status); + } + } + } + } + + if (form.getNewLabel() != null && form.getNewLabel().length() > 0) + { + SpecimenRequestStatus status = new SpecimenRequestStatus(); + status.setLabel(form.getNewLabel()); + List statuses = SpecimenRequestManager.get().getRequestStatuses(getContainer(), getUser()); + status.setSortOrder(statuses.size()); + status.setContainer(getContainer()); + status.setFinalState(form.isNewFinalState()); + status.setSpecimensLocked(form.isNewSpecimensLocked()); + SpecimenRequestManager.get().createRequestStatus(getUser(), status); + } + + StatusSettings settings = SettingsManager.get().getStatusSettings(getContainer()); + if (settings.isUseShoppingCart() != form.isUseShoppingCart()) + { + settings.setUseShoppingCart(form.isUseShoppingCart()); + SettingsManager.get().saveStatusSettings(getContainer(), settings); + } + return true; + } + + @Override + public ActionURL getSuccessURL(StatusEditForm form) + { + if (form.getNextPage() != null && form.getNextPage().length() > 0) + return new ActionURL(form.getNextPage()); + else + return getManageStudyURL(); + } + } + + @RequiresPermission(ManageSpecimenActorsPermission.class) + public class ManageStatusOrderAction extends DisplayManagementSubpageAction + { + public ManageStatusOrderAction() + { + super("manageStatusOrder", "Manage Status Order", "specimenRequest"); + } + + @Override + protected JspView getJspView(Study study) + { + return new JspView<>("/org/labkey/specimen/view/manageStatusOrder.jsp", study); + } + + @Override + public boolean handlePost(BulkEditForm form, BindException errors) + { + ensureSpecimenRequestsConfigured(false); + + String order = form.getOrder(); + if (order != null && order.length() > 0) + { + String[] rowIdStrings = order.split(","); + int[] rowIds = new int[rowIdStrings.length]; + for (int i = 0; i < rowIdStrings.length; i++) + rowIds[i] = Integer.parseInt(rowIdStrings[i]); + updateRequestStatusOrder(getContainer(), rowIds, false); + } + return true; + } + + @Override + public ActionURL getSuccessURL(BulkEditForm bulkEditForm) + { + return new ActionURL(ManageStatusesAction.class, getContainer()); + } + } + + @RequiresPermission(AdminPermission.class) + public class DeleteStatusAction extends FormHandlerAction + { + @Override + public void validateCommand(IdForm target, Errors errors) + { + } + + @Override + public boolean handlePost(IdForm form, BindException errors) throws Exception + { + List statuses = SpecimenRequestManager.get().getRequestStatuses(getContainer(), getUser()); + SpecimenRequestStatus status = SpecimenRequestManager.get().getRequestStatus(getContainer(), form.getId()); + if (status != null) + { + SpecimenRequestManager.get().deleteRequestStatus(status); + int[] remainingIds = new int[statuses.size() - 1]; + int idx = 0; + for (SpecimenRequestStatus remainingStatus : statuses) + { + if (remainingStatus.getRowId() != form.getId()) + remainingIds[idx++] = remainingStatus.getRowId(); + } + updateRequestStatusOrder(getContainer(), remainingIds, true); + } + return true; + } + + @Override + public ActionURL getSuccessURL(IdForm idForm) + { + return new ActionURL(ManageStatusesAction.class, getContainer()); + } + } + + public void writeExportData(SpecimenQueryView view, String type) throws IOException + { + switch (type) + { + case "excel" -> view.exportToExcel(getViewContext().getResponse()); + case "tsv" -> view.exportToTsv(getViewContext().getResponse()); + default -> throw new IllegalArgumentException(type + " is not a supported export type."); + } + } + + public static class ManageRequestForm extends IdForm + { + public enum PARAMS + { + newSite, + newActor, + newDescription, + export, + submissionResult, + returnUrl + } + + private Integer _newSite; + private Integer _newActor; + private String _newDescription; + private String _export; + private Boolean _submissionResult; + + public Integer getNewActor() + { + return _newActor; + } + + public void setNewActor(Integer newActor) + { + _newActor = newActor; + } + + public Integer getNewSite() + { + return _newSite; + } + + public void setNewSite(Integer newSite) + { + _newSite = newSite; + } + + public String getNewDescription() + { + return _newDescription; + } + + public void setNewDescription(String newDescription) + { + _newDescription = newDescription; + } + + public String getExport() + { + return _export; + } + + public void setExport(String export) + { + _export = export; + } + + public Boolean isSubmissionResult() + { + return _submissionResult; + } + + public void setSubmissionResult(Boolean submissionResult) + { + _submissionResult = submissionResult; + } + } + + @RequiresPermission(ReadPermission.class) + public class ManageRequestAction extends FormViewAction + { + private int _requestId; + + @Override + public void validateCommand(ManageRequestForm target, Errors errors) + { + } + + @Override + public ModelAndView getView(ManageRequestForm form, boolean reshow, BindException errors) throws Exception + { + SpecimenRequest request = SpecimenRequestManager.get().getRequest(getContainer(), form.getId()); + if (request == null) + throw new NotFoundException(); + + _requestId = request.getRowId(); + ManageRequestBean bean = new ManageRequestBean(getViewContext(), request, form.getExport() != null, + form.isSubmissionResult(), form.getReturnUrl()); + if (form.getExport() != null) + { + bean.getSpecimenQueryView().getSettings().setMaxRows(Table.ALL_ROWS); // #34998; exporting specimens in a request should include all of them + writeExportData(bean.getSpecimenQueryView(), form.getExport()); + return null; + } + else + { + GridView attachmentsGrid = getRequestEventAttachmentsGridView(getViewContext().getRequest(), errors, _requestId); + SpecimenQueryView queryView = bean.getSpecimenQueryView(); + if (null != queryView) + queryView.setTitle("Associated Specimens"); + HBox hbox = new HBox(new JspView<>("/org/labkey/specimen/view/manageRequest.jsp", bean), attachmentsGrid); + hbox.setTableWidth(""); + return new VBox(hbox, queryView); + } + } + + @Override + public boolean handlePost(ManageRequestForm form, BindException errors) throws Exception + { + if (!getContainer().hasPermission(getUser(), ManageRequestsPermission.class)) + throw new UnauthorizedException("You do not have permissions to create new specimen request requirements!"); + + SpecimenRequest request = SpecimenRequestManager.get().getRequest(getContainer(), form.getId()); + if (request == null) + throw new NotFoundException(); + + if (form.getNewActor() != null && form.getNewActor() > 0) + { + SpecimenRequestActor actor = SpecimenRequestRequirementProvider.get().getActor(getContainer(), form.getNewActor()); + if (actor != null) + { + // an actor is valid if a site has been provided for a per-site actor, or if no site + // has been provided for a non-site-specific actor. The UI should enforce this already, + // so this is just a backup check. + boolean validActor = (actor.isPerSite() && form.getNewSite() != null && form.getNewSite() > 0) || + (!actor.isPerSite() && (form.getNewSite() == null || form.getNewSite() <= 0)); + if (validActor) + { + SpecimenRequestRequirement requirement = new SpecimenRequestRequirement(); + requirement.setContainer(getContainer()); + if (form.getNewSite() != null && form.getNewSite() > 0) + requirement.setSiteId(form.getNewSite()); + requirement.setActorId(form.getNewActor()); + requirement.setDescription(form.getNewDescription()); + requirement.setRequestId(request.getRowId()); + SpecimenRequestManager.get().createRequestRequirement(getUser(), requirement, true, true); + } + } + } + return true; + } + + @Override + public ActionURL getSuccessURL(ManageRequestForm manageRequestForm) + { + return getManageRequestURL(manageRequestForm.getId(), manageRequestForm.getReturnActionURL()); + } + + @Override + public void addNavTrail(NavTree root) + { + addSpecimenRequestNavTrail(root, _requestId); + } + } + + private static class AttachmentDisplayColumn extends SimpleDisplayColumn + { + private final HttpServletRequest _request; + + public AttachmentDisplayColumn(HttpServletRequest request) + { + super(); + _request = request; + setCaption("Attachments"); + } + + @Override + public void renderGridCellContents(RenderContext ctx, Writer out) throws IOException + { + Map cols = ctx.getRow(); + SpecimenRequestEvent event = ObjectFactory.Registry.getFactory(SpecimenRequestEvent.class).fromMap(cols); + Collection attachments = AttachmentService.get().getAttachments(event); + + if (!attachments.isEmpty()) + { + for (Attachment attachment : attachments) + { + out.write(""); + out.write(""); + out.write(PageFlowUtil.filter(attachment.getName())); + out.write("
"); + } + } + else + out.write(" "); + } + } + + public GridView getRequestEventAttachmentsGridView(HttpServletRequest request, BindException errors, int requestId) + { + DataRegion rgn = new DataRegion() { + private int i = 0; + + @Override + protected void renderTableRow(RenderContext ctx, Writer out, boolean showRecordSelectors, List renderers, int rowIndex) throws SQLException, IOException + { + // This is so we don't show rows that have no attachments + SpecimenRequestEvent event = ObjectFactory.Registry.getFactory(SpecimenRequestEvent.class).fromMap(ctx.getRow()); + List attachments = AttachmentService.get().getAttachments(event); + if (!attachments.isEmpty()) + { + super.renderTableRow(ctx, out, showRecordSelectors, renderers, i++); + } + } + }; + + TableInfo tableInfoRequestEvent = SpecimenSchema.get().getTableInfoSampleRequestEvent(); + rgn.setTable(tableInfoRequestEvent); + rgn.setColumns(tableInfoRequestEvent.getColumns("Created", "EntityId")); + rgn.getDisplayColumn("EntityId").setVisible(false); + rgn.getDisplayColumn("Created").setVisible(false); + rgn.setShowBorders(true); + rgn.setShowPagination(false); + DisplayColumn attachmentDisplayColumn = new AttachmentDisplayColumn(request); + rgn.addDisplayColumn(attachmentDisplayColumn); + GridView grid = new GridView(rgn, errors); + SimpleFilter filter = new SimpleFilter(FieldKey.fromString("RequestId"), requestId); + filter.addCondition(FieldKey.fromString("RequirementId"), null, CompareType.ISBLANK); // if null, then event is NOT a requirement + grid.setFilter(filter); + FieldKey fieldKey = FieldKey.fromString("Created"); + Sort sort = new Sort(); + sort.insertSortColumn(fieldKey, Sort.SortDirection.DESC); + grid.setSort(sort); + return grid; + } + + public static class ModifySpecimenRequestForm extends IdForm implements HiddenFormInputGenerator + { + public enum PARAMS + { + specimenIds, + returnUrl + } + + private String _specimenIds; + + public String getSpecimenIds() + { + return _specimenIds; + } + + public void setSpecimenIds(String specimenIds) + { + _specimenIds = specimenIds; + } + + @Override + public String getHiddenFormInputs(ViewContext ctx) + { + StringBuilder builder = new StringBuilder(); + if (getId() != 0) + builder.append("\n"); + if (_specimenIds != null) + builder.append(""); + return builder.toString(); + } + } + + private void requiresEditRequestPermissions(SpecimenRequest request) + { + if (!SpecimenRequestManager.get().hasEditRequestPermissions(getUser(), request)) + throw new UnauthorizedException(); + } + + // Additional permission checking takes place before handlePost() + @RequiresPermission(ReadPermission.class) + public class RemoveRequestSpecimensAction extends FormHandlerAction + { + @Override + public void validateCommand(ModifySpecimenRequestForm target, Errors errors) + { + } + + @Override + public boolean handlePost(ModifySpecimenRequestForm form, BindException errors) throws Exception + { + SpecimenRequest request = SpecimenRequestManager.get().getRequest(getContainer(), form.getId()); + requiresEditRequestPermissions(request); + long[] ids = toLongArray(DataRegionSelection.getSelected(getViewContext(), true)); + List specimenIds = new ArrayList<>(); + for (long id : ids) + specimenIds.add(id); + try + { + SpecimenRequestManager.get().deleteRequestSpecimenMappings(getUser(), request, specimenIds, true); + } + catch (RequestabilityManager.InvalidRuleException e) + { + errors.reject(ERROR_MSG, "The specimens could not be removed because a requestability rule is configured incorrectly. " + + "Please report this problem to an administrator. Error details: " + e.getMessage()); + return false; + } + return true; + } + + @Override + public ActionURL getSuccessURL(ModifySpecimenRequestForm addToSpecimenRequestForm) + { + return getManageRequestURL(addToSpecimenRequestForm.getId(), addToSpecimenRequestForm.getReturnActionURL()); + } + } + + @RequiresPermission(RequestSpecimensPermission.class) + public class ImportVialIdsAction extends AbstractQueryImportAction + { + private int _requestId = -1; + + @Override + protected void initRequest(IdForm form) throws ServletException + { + _requestId = form.getId(); + setHasColumnHeaders(false); + setImportMessage("Upload a list of Global Unique Identifiers from a TXT, CSV or Excel file or paste the list directly into the text box below. " + + "The list must have only one column and no header row."); + setNoTableInfo(); + setHideTsvCsvCombo(true); + } + + @Override + public ModelAndView getView(IdForm form, BindException errors) throws Exception + { + initRequest(form); + SpecimenRequest request = SpecimenRequestManager.get().getRequest(getContainer(), _requestId); + if (request == null) + throw new NotFoundException(); + + if (!SpecimenRequestManager.get().hasEditRequestPermissions(getUser(), request) || + SpecimenRequestManager.get().isInFinalState(request)) + { + return new HtmlView("
You do not have permissions to modify this request.
"); + } + + return getDefaultImportView(form, errors); + } + + @Override + protected void validatePermission(User user, BindException errors) + { + checkPermissions(); + SpecimenRequest request = SpecimenRequestManager.get().getRequest(getContainer(), _requestId); + + if (!SpecimenRequestManager.get().hasEditRequestPermissions(getUser(), request) || + SpecimenRequestManager.get().isInFinalState(request)) + { + // No permission + errors.reject(SpringActionController.ERROR_MSG, "You do not have permission to modify this request."); + } + } + + @Override + protected boolean canInsert(User user) + { + return getContainer().hasPermission(user, RequestSpecimensPermission.class); + } + + @Override + protected int importData(DataLoader dl, FileStream file, String originalName, BatchValidationException errors, @Nullable AuditBehaviorType auditBehaviorType, @Nullable TransactionAuditProvider.TransactionAuditEvent auditEvent) throws IOException + { + List errorList = new LinkedList<>(); + + ColumnDescriptor[] columns = new ColumnDescriptor[2]; // Only 1 actual column of type String + columns[0] = new ColumnDescriptor("Actual", String.class); + columns[1] = new ColumnDescriptor("Dummy", String.class); + dl.setColumns(columns); + + List> rows = dl.load(); + String[] globalIds = new String[rows.size()]; + int i = 0; + for (Map row : rows) + { + String idActual = (String)row.get("Actual"); + if (idActual == null) + { + errorList.add("Malformed input data."); + break; + } + + String idDummy = (String)row.get("Dummy"); + if (idDummy != null) + { + errorList.add("Only one id per line is allowed."); + break; + } + globalIds[i] = idActual; + i += 1; + } + + if (errorList.size() == 0) + { + // Get all the specimen objects. If it throws an exception then there was an error and we'll + // root around to figure out what to report + SpecimenManagerNew specimenManager = SpecimenManagerNew.get(); + try + { + SpecimenRequest request = SpecimenRequestManager.get().getRequest(getContainer(), _requestId); + List vials = SpecimenManagerNew.get().getVials(getContainer(), getUser(), globalIds); + + if (vials != null && vials.size() == globalIds.length) + { + // All the specimens exist; + // Check for Availability and then add them to the request. + // There still may be errors (like someone already has requested that specimen) which will be + // caught by createRequestSpecimenMapping + + for (Vial s : vials) + { + if (!s.isAvailable()) // || s.isLockedInRequest()) + { + errorList.add(RequestabilityManager.makeSpecimenUnavailableMessage(s, null)); + } + } + + if (errorList.size() == 0) + { + ArrayList vialList = new ArrayList<>(vials.size()); + vialList.addAll(vials); + SpecimenRequestManager.get().createRequestSpecimenMapping(getUser(), request, vialList, true, true); + } + } + else + { + errorList.add("Duplicate Ids found."); + } + } + catch (RequestabilityManager.InvalidRuleException e) + { + errorList.add("The request could not be created because a requestability rule is configured incorrectly. " + + "Please report this problem to an administrator. Error details: " + e.getMessage()); + } + catch (SpecimenRequestException e) + { + // There was an error; some id had no specimen matching + boolean hasSpecimenError = false; + for (String id : globalIds) + { + Vial vial = specimenManager.getVial(getContainer(), getUser(), id); + if (vial == null) + { + errorList.add("Specimen " + id + " not found."); + hasSpecimenError = true; + } + } + + if (!hasSpecimenError) + { // Expected one of them to not be found, so this is unusual + errorList.add("Error adding all of the specimens together."); + } + } + } + + if (!errorList.isEmpty()) + { + for (String error : errorList) + errors.addRowError(new ValidationException(error)); + } + + if (errors.hasErrors()) + return 0; + return rows.size(); + } + + @Override + public void addNavTrail(NavTree root) + { + addSpecimenRequestNavTrail(root, _requestId); + root.addChild("Upload Specimen Identifiers"); + } + + @Override + protected ActionURL getSuccessURL(IdForm form) + { + return getManageRequestURL(getContainer(), _requestId, null); + } + } + + public static class ManageRequestStatusForm extends IdForm + { + private int _status; + private String _comments; + private String _requestDescription; + private String[] _notificationIdPairs; + private boolean _emailInactiveUsers; + + public int getStatus() + { + return _status; + } + + public void setStatus(int status) + { + _status = status; + } + + public String getComments() + { + return _comments; + } + + public void setComments(String comments) + { + _comments = comments; + } + + public String[] getNotificationIdPairs() + { + return _notificationIdPairs; + } + + public void setNotificationIdPairs(String[] notificationIdPairs) + { + _notificationIdPairs = notificationIdPairs; + } + + public String getRequestDescription() + { + return _requestDescription; + } + + public void setRequestDescription(String requestDescription) + { + _requestDescription = requestDescription; + } + + public boolean isEmailInactiveUsers() + { + return _emailInactiveUsers; + } + + public void setEmailInactiveUsers(boolean emailInactiveUsers) + { + _emailInactiveUsers = emailInactiveUsers; + } + } + + private List getNotifications(SpecimenRequest specimenRequest, String[] notificationIdPairs) + { + List siteActors = new ArrayList<>(); + if (notificationIdPairs == null || notificationIdPairs.length == 0) + return siteActors; + for (String notificationIdPair : notificationIdPairs) + siteActors.add(ActorNotificationRecipientSet.getFromFormValue(specimenRequest.getContainer(), notificationIdPair)); + return siteActors; + } + + private void sendNewRequestNotifications(SpecimenRequest request, BindException errors) throws Exception + { + RequestNotificationSettings settings = SettingsManager.get().getRequestNotificationSettings(request.getContainer()); + Address[] notify = settings.getNewRequestNotifyAddresses(); + if (notify != null && notify.length > 0) + { + SpecimenRequestEvent event = SpecimenRequestManager.get().createRequestEvent(getUser(), request, + RequestEventType.REQUEST_CREATED, null, Collections.emptyList()); + DefaultRequestNotification notification = new DefaultRequestNotification(request, Collections.singletonList(new NotificationRecipientSet(notify)), + "New Request Created", event, null, null, getViewContext()); + sendNotification(notification, true, errors); + } + } + + private void sendNotification(DefaultRequestNotification notification, boolean includeInactiveUsers, BindException errors) throws Exception + { + RequestNotificationSettings settings = SettingsManager.get().getRequestNotificationSettings(getContainer()); + + SpecimenRequest specimenRequest = notification.getSpecimenRequest(); + String specimenList = null; + if (RequestNotificationSettings.SpecimensAttachmentEnum.InEmailBody == settings.getSpecimensAttachmentEnum()) + { + specimenList = notification.getSpecimenListHTML(getViewContext()); + } + + NotificationBean notificationBean = new NotificationBean(getViewContext(), notification, specimenList, getStudy().getLabel()); + SpecimenRequestNotificationEmailTemplate template = EmailTemplateService.get().getEmailTemplate(SpecimenRequestNotificationEmailTemplate.class, getContainer()); + template.init(notificationBean); + if (settings.isReplyToCurrentUser()) + template.setOriginatingUser(getUser()); + + MailHelper.MultipartMessage message = MailHelper.createMultipartMessage(); + template.renderSenderToMessage(message, getContainer()); + message.setEncodedHtmlContent(template.renderBody(getContainer())); + message.setSubject(template.renderSubject(getContainer())); + + boolean first = true; + for (NotificationRecipientSet recipient : notification.getRecipients()) + { + for (String email : recipient.getEmailAddresses(includeInactiveUsers)) + { + if (first) + { + Address[] ccAddresses = settings.getCCAddresses(); + if (ccAddresses != null && ccAddresses.length > 0) + message.setRecipients(Message.RecipientType.CC, ccAddresses); + first = false; + } + else + message.setRecipients(Message.RecipientType.CC, ""); + + try + { + message.setRecipient(Message.RecipientType.TO, new InternetAddress(email)); + MailHelper.send(message, getUser(), getContainer()); + } + catch (javax.mail.internet.AddressException | NullPointerException e) + { + errors.reject(SpringActionController.ERROR_MSG, e.getMessage() == null ? e.toString() : e.getMessage()); // Bad address; also InternetAddress constructor can throw null + } + } + if (notification.getRequirement() != null) + SpecimenRequestManager.get().createRequestEvent(getUser(), notification.getRequirement(), + RequestEventType.NOTIFICATION_SENT, "Notification sent to " + recipient.getLongRecipientDescription(), null); + else + SpecimenRequestManager.get().createRequestEvent(getUser(), specimenRequest, + RequestEventType.NOTIFICATION_SENT, "Notification sent to " + recipient.getLongRecipientDescription(), null); + } + } + + public static ActionURL getManageRequestStatusURL(Container c, int requestId) + { + return new ActionURL(ManageRequestStatusAction.class, c).addParameter("id", requestId); + } + + @RequiresPermission(RequestSpecimensPermission.class) + public class ManageRequestStatusAction extends FormViewAction + { + private SpecimenRequest _specimenRequest; + + @Override + public void validateCommand(ManageRequestStatusForm target, Errors errors) + { + } + + @Override + public ModelAndView getView(ManageRequestStatusForm form, boolean reshow, BindException errors) + { + _specimenRequest = SpecimenRequestManager.get().getRequest(getContainer(), form.getId()); + if (_specimenRequest == null) + throw new NotFoundException(); + + return new JspView<>("/org/labkey/specimen/view/manageRequestStatus.jsp", + new ManageRequestBean(getViewContext(), _specimenRequest, false, null, null), errors); + } + + @Override + public boolean handlePost(final ManageRequestStatusForm form, BindException errors) throws Exception + { + _specimenRequest = SpecimenRequestManager.get().getRequest(getContainer(), form.getId()); + if (_specimenRequest == null) + throw new NotFoundException(); + + boolean statusChanged = form.getStatus() != _specimenRequest.getStatusId(); + boolean detailsChanged = !nullSafeEqual(form.getRequestDescription(), _specimenRequest.getComments()); + + List files = getAttachmentFileList(); + boolean hasAttachments = !files.isEmpty(); + + boolean hasComments = form.getComments() != null && form.getComments().length() > 0; + if (statusChanged || detailsChanged || hasComments || hasAttachments) + { + RequestEventType changeType; + String comment = ""; + String eventSummary; + if (statusChanged || detailsChanged) + { + if (statusChanged) + { + SpecimenRequestStatus prevStatus = SpecimenRequestManager.get().getRequestStatus(getContainer(), _specimenRequest.getStatusId()); + SpecimenRequestStatus newStatus = SpecimenRequestManager.get().getRequestStatus(getContainer(), form.getStatus()); + comment += "Status changed from \"" + (prevStatus != null ? prevStatus.getLabel() : "N/A") + "\" to \"" + + (newStatus != null ? newStatus.getLabel() : "N/A") + "\"\n"; + } + if (detailsChanged) + { + String prevDetails = _specimenRequest.getComments(); + String newDetails = form.getRequestDescription(); + comment += "Description changed from \"" + (prevDetails != null ? prevDetails : "N/A") + "\" to \"" + + (newDetails != null ? newDetails : "N/A") + "\"\n"; + } + eventSummary = comment; + if (hasComments) + comment += form.getComments(); + _specimenRequest = _specimenRequest.createMutable(); + _specimenRequest.setStatusId(form.getStatus()); + _specimenRequest.setComments(form.getRequestDescription()); + _specimenRequest.setModified(new Date()); + try + { + SpecimenRequestManager.get().updateRequest(getUser(), _specimenRequest); + } + catch (RequestabilityManager.InvalidRuleException e) + { + errors.reject(ERROR_MSG, "The request could not be updated because a requestability rule is configured incorrectly. " + + "Please report this problem to an administrator. Error details: " + e.getMessage()); + return false; + } + changeType = RequestEventType.REQUEST_STATUS_CHANGED; + } + else + { + changeType = RequestEventType.COMMENT_ADDED; + comment = form.getComments(); + eventSummary = "Comments added."; + } + + SpecimenRequestEvent event; + try + { + event = SpecimenRequestManager.get().createRequestEvent(getUser(), _specimenRequest, changeType, comment, files); + } + catch (Exception e) + { + errors.reject(ERROR_MSG, "The request could not be updated because of an unexpected error. " + + "Please report this problem to an administrator. Error details: " + e.getMessage()); + return false; + } + try + { + List recipients = getNotifications(_specimenRequest, form.getNotificationIdPairs()); + DefaultRequestNotification notification = new DefaultRequestNotification(_specimenRequest, recipients, + eventSummary, event, form.getComments(), null, getViewContext()); + sendNotification(notification, form.isEmailInactiveUsers(), errors); + } + catch (ConfigurationException | IOException e) + { + errors.reject(ERROR_MSG, "The request was updated successfully, but the notification failed: " + e.getMessage()); + return false; + } + } + + return !errors.hasErrors(); + } + + @Override + public ActionURL getSuccessURL(ManageRequestStatusForm manageRequestForm) + { + return getManageRequestURL(_specimenRequest.getRowId(), null); + } + + @Override + public void addNavTrail(NavTree root) + { + addSpecimenRequestNavTrail(root, _specimenRequest.getRowId()); + root.addChild("Update Request"); + } + } + + private enum CommentsConflictResolution + { + SKIP, + APPEND, + REPLACE + } + + public static class UpdateSpecimenCommentsForm + { + private String _comments; + private int[] _rowId; + private boolean _fromGroupedView; + private String _referrer; + private boolean _saveCommentsPost; + private String _conflictResolve; + private Boolean _qualityControlFlag; + + public String getComments() + { + return _comments; + } + + public void setComments(String comments) + { + _comments = comments; + } + + public int[] getRowId() + { + return _rowId; + } + + public void setRowId(int[] rowId) + { + _rowId = rowId; + } + + public boolean isFromGroupedView() + { + return _fromGroupedView; + } + + public void setFromGroupedView(boolean fromGroupedView) + { + _fromGroupedView = fromGroupedView; + } + + public String getReferrer() + { + return _referrer; + } + + public void setReferrer(String referrer) + { + _referrer = referrer; + } + + public boolean isSaveCommentsPost() + { + return _saveCommentsPost; + } + + public void setSaveCommentsPost(boolean saveCommentsPost) + { + _saveCommentsPost = saveCommentsPost; + } + + public String getConflictResolve() + { + return _conflictResolve; + } + + public void setConflictResolve(String conflictResolve) + { + _conflictResolve = conflictResolve; + } + + public CommentsConflictResolution getConflictResolveEnum() + { + return _conflictResolve == null ? CommentsConflictResolution.REPLACE : CommentsConflictResolution.valueOf(_conflictResolve); + } + + public Boolean isQualityControlFlag() + { + return _qualityControlFlag; + } + + public void setQualityControlFlag(Boolean qualityControlFlag) + { + _qualityControlFlag = qualityControlFlag; + } + } + + public static class UpdateParticipantCommentsForm extends UpdateSpecimenCommentsForm + { + private boolean _copyToParticipant; + private boolean _deleteVialComment; + private int _copySpecimenId; + private String _copyParticipantId; + + public boolean isCopyToParticipant() + { + return _copyToParticipant; + } + + public void setCopyToParticipant(boolean copyToParticipant) + { + _copyToParticipant = copyToParticipant; + } + + public boolean isDeleteVialComment() + { + return _deleteVialComment; + } + + public void setDeleteVialComment(boolean deleteVialComment) + { + _deleteVialComment = deleteVialComment; + } + + public int getCopySampleId() + { + return _copySpecimenId; + } + + public void setCopySampleId(int copySpecimenId) + { + _copySpecimenId = copySpecimenId; + } + + public String getCopyParticipantId() + { + return _copyParticipantId; + } + + public void setCopyParticipantId(String copyParticipantId) + { + _copyParticipantId = copyParticipantId; + } + } + + @RequiresPermission(SetSpecimenCommentsPermission.class) + public class ClearCommentsAction extends FormHandlerAction + { + @Override + public void validateCommand(UpdateSpecimenCommentsForm target, Errors errors) + { + } + + @Override + public boolean handlePost(UpdateSpecimenCommentsForm updateSpecimenCommentsForm, BindException errors) throws Exception + { + List selectedVials = getSpecimensFromPost(updateSpecimenCommentsForm.isFromGroupedView(), false); + if (selectedVials != null) + { + for (Vial vial : selectedVials) + { + // use the currently saved history conflict state; if it's been forced before, this will prevent it + // from being cleared. + SpecimenComment comment = SpecimenManager.get().getSpecimenCommentForVial(vial); + if (comment != null) + { + SpecimenManager.get().setSpecimenComment(getUser(), vial, null, + comment.isQualityControlFlag(), comment.isQualityControlFlagForced()); + } + } + } + return true; + } + + @Override + public ActionURL getSuccessURL(UpdateSpecimenCommentsForm updateSpecimenCommentsForm) + { + return new ActionURL(updateSpecimenCommentsForm.getReferrer()); + } + } + + @RequiresPermission(SetSpecimenCommentsPermission.class) + public class UpdateCommentsAction extends FormViewAction + { + private ActionURL _successUrl; + + @Override + public void validateCommand(UpdateParticipantCommentsForm specimenCommentsForm, Errors errors) + { + if (specimenCommentsForm.isSaveCommentsPost() && + (specimenCommentsForm.getRowId() == null || + specimenCommentsForm.getRowId().length == 0)) + { + errors.reject(null, "No vials were selected."); + } + } + + @Override + public ModelAndView getView(UpdateParticipantCommentsForm specimenCommentsForm, boolean reshow, BindException errors) + { + List selectedVials = getSpecimensFromPost(specimenCommentsForm.isFromGroupedView(), false); + + if (selectedVials == null || selectedVials.size() == 0) + { + // are the vial IDs on the URL? + int[] rowId = specimenCommentsForm.getRowId(); + if (rowId != null && rowId.length > 0) + { + try + { + List vials = SpecimenManagerNew.get().getVials(getContainer(), getUser(), rowId); + selectedVials = new ArrayList<>(vials); + } + catch (SpecimenRequestException e) + { + errors.reject(ERROR_MSG, e.getMessage()); + } + } + if (selectedVials == null || selectedVials.size() == 0) + return new HtmlView("No vials selected. " + PageFlowUtil.link("back").href("javascript:back()")); + } + + return new JspView<>("/org/labkey/specimen/view/updateComments.jsp", + new UpdateSpecimenCommentsBean(getViewContext(), selectedVials, specimenCommentsForm.getReferrer()), errors); + } + + @Override + public boolean handlePost(UpdateParticipantCommentsForm commentsForm, BindException errors) throws Exception + { + if (commentsForm.getRowId() == null) + return false; + + User user = getUser(); + Container container = getContainer(); + List vials = new ArrayList<>(); + for (int rowId : commentsForm.getRowId()) + vials.add(SpecimenManagerNew.get().getVial(container, user, rowId)); + + Map currentComments = SpecimenManager.get().getSpecimenComments(vials); + + // copying or moving vial comments to participant level comments + if (commentsForm.isCopyToParticipant()) + { + if (commentsForm.getCopySampleId() != -1) + { + Vial vial = SpecimenManagerNew.get().getVial(container, user, commentsForm.getCopySampleId()); + if (vial != null) + { + _successUrl = new ActionURL(urlProvider(SpecimenUrls.class).getCopyParticipantCommentActionClass(), container). + addParameter(ParticipantCommentForm.params.participantId, vial.getPtid()). + addParameter(ParticipantCommentForm.params.visitId, String.valueOf(vial.getVisitValue())). + addParameter(ParticipantCommentForm.params.comment, commentsForm.getComments()). + addReturnURL(new URLHelper(commentsForm.getReferrer())); + } + } + else + { + _successUrl = new ActionURL(urlProvider(SpecimenUrls.class).getCopyParticipantCommentActionClass(), container). + addParameter(ParticipantCommentForm.params.participantId, commentsForm.getCopyParticipantId()). + addParameter(ParticipantCommentForm.params.comment, commentsForm.getComments()). + addReturnURL(new URLHelper(commentsForm.getReferrer())); + } + + // delete existing vial comments if move is specified + if (commentsForm.isDeleteVialComment()) + { + for (Vial vial : vials) + { + SpecimenComment comment = SpecimenManager.get().getSpecimenCommentForVial(vial); + if (comment != null) + { + _successUrl.addParameter(ParticipantCommentForm.params.vialCommentsToClear, vial.getRowId()); + } + } + } + return true; + } + + for (Vial vial : vials) + { + SpecimenComment previousComment = currentComments.get(vial); + + boolean newConflictState; + boolean newForceState; + if (commentsForm.isQualityControlFlag() != null && SettingsManager.get().getDisplaySettings(container).isEnableManualQCFlagging()) + { + // if a state has been specified in the post, we consider this to be 'forcing' the state: + newConflictState = commentsForm.isQualityControlFlag().booleanValue(); + newForceState = true; + } + else + { + // if we aren't forcing the state, just re-save whatever was previously saved: + newConflictState = previousComment != null && previousComment.isQualityControlFlag(); + newForceState = previousComment != null && previousComment.isQualityControlFlagForced(); + } + + if (previousComment == null || commentsForm.getConflictResolveEnum() == CommentsConflictResolution.REPLACE) + { + SpecimenManager.get().setSpecimenComment(getUser(), vial, commentsForm.getComments(), + newConflictState, newForceState); + } + else if (commentsForm.getConflictResolveEnum() == CommentsConflictResolution.APPEND) + { + SpecimenManager.get().setSpecimenComment(getUser(), vial, previousComment.getComment() + "\n" + commentsForm.getComments(), + newConflictState, newForceState); + } + // If we haven't updated by now, our user has selected CommentsConflictResolution.SKIP and previousComments is non-null + // so we no-op for this vial. + } + _successUrl = new ActionURL(commentsForm.getReferrer()); + return true; + } + + @Override + public ActionURL getSuccessURL(UpdateParticipantCommentsForm commentsForm) + { + return _successUrl; + } + + @Override + public void addNavTrail(NavTree root) + { + addBaseSpecimenNavTrail(root); + root.addChild("Set vial comments"); + } + } + + @RequiresPermission(RequestSpecimensPermission.class) + public class SubmitRequestAction extends FormHandlerAction + { + @Override + public void validateCommand(IdForm target, Errors errors) + { + } + + @Override + public boolean handlePost(IdForm form, BindException errors) throws Exception + { + if (!SettingsManager.get().isSpecimenShoppingCartEnabled(getContainer())) + throw new UnauthorizedException(); + + SpecimenRequest request = SpecimenRequestManager.get().getRequest(getContainer(), form.getId()); + requiresEditRequestPermissions(request); + List vials = request.getVials(); + if (!vials.isEmpty() || SpecimenService.get().getRequestCustomizer().allowEmptyRequests()) + { + SpecimenRequestStatus newStatus = SpecimenRequestManager.get().getInitialRequestStatus(getContainer(), getUser(), true); + request = request.createMutable(); + request.setStatusId(newStatus.getRowId()); + try + { + SpecimenRequestManager.get().updateRequest(getUser(), request); + SpecimenRequestManager.get().createRequestEvent(getUser(), request, + RequestEventType.REQUEST_STATUS_CHANGED, "Request submitted for processing.", null); + if (SettingsManager.get().isSpecimenShoppingCartEnabled(getContainer())) + { + try + { + sendNewRequestNotifications(request, errors); + } + catch (ConfigurationException e) + { + errors.reject(ERROR_MSG, e.getMessage()); + } + } + } + catch (RequestabilityManager.InvalidRuleException e) + { + errors.reject(ERROR_MSG, "The request could not be submitted because a requestability rule is configured incorrectly. " + + "Please report this problem to an administrator. Error details: " + e.getMessage()); + } + catch (IOException e) + { + errors.reject(ERROR_MSG, e.getMessage()); + } + } + else + { + errors.addError(new ObjectError("Specimen Request", new String[] {"NullError"}, null, "Only requests containing specimens can be submitted.")); + } + + return !errors.hasErrors(); + } + + @Override + public ActionURL getSuccessURL(IdForm idForm) + { + return getManageRequestURL(idForm.getId(), null).addParameter("submissionResult", true); + } + } + + @RequiresPermission(RequestSpecimensPermission.class) + public class DeleteRequestAction extends FormHandlerAction + { + @Override + public void validateCommand(IdForm target, Errors errors) + { + } + + @Override + public boolean handlePost(IdForm form, BindException errors) throws Exception + { + SpecimenRequest request = SpecimenRequestManager.get().getRequest(getContainer(), form.getId()); + requiresEditRequestPermissions(request); + SpecimenRequestStatus cartStatus = SpecimenRequestManager.get().getRequestShoppingCartStatus(getContainer(), getUser()); + if (request.getStatusId() != cartStatus.getRowId()) + throw new UnauthorizedException(); + + try + { + SpecimenRequestManager.get().deleteRequest(getUser(), request); + } + catch (RequestabilityManager.InvalidRuleException e) + { + errors.reject(ERROR_MSG, "The request could not be canceled because a requestability rule is configured incorrectly. " + + "Please report this problem to an administrator. Error details: " + e.getMessage()); + } + return true; + } + + @Override + public ActionURL getSuccessURL(IdForm form) + { + return new ActionURL(ViewRequestsAction.class, getContainer()); + } + } + + private ActionURL getSpecimensURL() + { + return getSpecimensURL(getContainer()); + } + + public static ActionURL getSpecimensURL(Container c) + { + return new ActionURL(SpecimensAction.class, c); + } + + public static ActionURL getSpecimensURL(Container c, boolean showVials) + { + return getSpecimensURL(c).addParameter(SpecimenViewTypeForm.PARAMS.showVials, showVials); + } + + public static ActionURL getCommentURL(Container c, String globalUniqueId) + { + return getSpecimensURL(c, true) + .addParameter(SpecimenViewTypeForm.PARAMS.viewMode, SpecimenQueryView.Mode.COMMENTS.name()) + .addParameter("SpecimenDetail.GlobalUniqueId~eq", globalUniqueId); + } + + @RequiresPermission(ReadPermission.class) + @ActionNames("specimens,samples") + public class SpecimensAction extends QueryViewAction + { + private boolean _vialView; + + public SpecimensAction() + { + super(SpecimenViewTypeForm.class); + } + + @Override + protected SpecimenQueryView createQueryView(SpecimenViewTypeForm form, BindException errors, boolean forExport, String dataRegion) + { + Study study = getStudyThrowIfNull(); + + _vialView = form.isShowVials(); + CohortFilter cohortFilter = CohortService.get().getFromURL(getContainer(), getUser(), getViewContext().getActionURL(), _vialView ? "SpecimenDetail" : "SpecimenSummary"); + SpecimenQueryView view = getSpecimenQueryView(_vialView, forExport, null, form.getViewModeEnum(), cohortFilter); + if (isCommentsMode(getContainer(), form.getViewModeEnum())) + view.setRestrictRecordSelectors(false); + return view; + } + + @Override + protected ModelAndView getHtmlView(SpecimenViewTypeForm form, BindException errors) throws Exception + { + Study study = getStudyRedirectIfNull(); + + SpecimenQueryView view = createInitializedQueryView(form, errors, form.getExportType() != null, null); + SpecimenHeaderBean bean = new SpecimenHeaderBean(getViewContext(), view); + // Get last selected request + Integer lastSpecimenRequest = StudyInternalService.get().getLastSpecimenRequest(study); + if (null != lastSpecimenRequest) + bean.setSelectedRequest(lastSpecimenRequest); + JspView header = new JspView<>("/org/labkey/specimen/view/specimenHeader.jsp", bean); + return new VBox(header, view); + } + + @Override + public void addNavTrail(NavTree root) + { + addBaseSpecimenNavTrail(root); + root.addChild(_vialView ? "Vials" : "Grouped Vials"); + } + } + + @RequiresPermission(ReadPermission.class) + public class RequestHistoryAction extends SimpleViewAction + { + private int _requestId; + + @Override + public ModelAndView getView(IdForm form, BindException errors) + { + _requestId = form.getId(); + HtmlView header = new HtmlView(new Link.LinkBuilder("View Request").href(SpecimenController.getManageRequestURL(getContainer(), form.getId(), null))); + SimpleFilter filter = new SimpleFilter(FieldKey.fromParts("RequestId"), form.getId()); + GridView historyGrid = getRequestEventGridView(getViewContext().getRequest(), errors, filter); + return new VBox(header, historyGrid); + } + + @Override + public void addNavTrail(NavTree root) + { + addSpecimenRequestNavTrail(root, _requestId); + root.addChild("Request History"); + } + } + + @RequiresPermission(ManageNewRequestFormPermission.class) + public class ManageRequestInputsAction extends SimpleViewAction + { + @Override + public ModelAndView getView(PipelineForm pipelineForm, BindException errors) throws Exception + { + ensureSpecimenRequestsConfigured(false); + return new JspView<>("/org/labkey/specimen/view/manageRequestInputs.jsp", + new ManageRequestInputsBean(getViewContext())); + } + + @Override + public void addNavTrail(NavTree root) + { + setHelpTopic("coordinateSpecimens#form"); + addManageStudyNavTrail(root); + root.addChild("Manage New Request Form"); + } + } + + public static final class ManageRequestInputsForm + { + private String[] _title; + private String[] _helpText; + private int[] _multiline; + private int[] _required; + private int[] _rememberSiteValue; + + public String[] getHelpText() + { + return _helpText; + } + + public void setHelpText(String[] helpText) + { + _helpText = helpText; + } + + public String[] getTitle() + { + return _title; + } + + public void setTitle(String[] title) + { + _title = title; + } + + public int[] getMultiline() + { + return _multiline; + } + + public void setMultiline(int[] multiline) + { + _multiline = multiline; + } + + public int[] getRememberSiteValue() + { + return _rememberSiteValue; + } + + public void setRememberSiteValue(int[] rememberSiteValue) + { + _rememberSiteValue = rememberSiteValue; + } + + public int[] getRequired() + { + return _required; + } + + public void setRequired(int[] required) + { + _required = required; + } + } + + @RequiresPermission(ManageNewRequestFormPermission.class) + public class HandleUpdateRequestInputsAction extends FormHandlerAction + { + @Override + public void validateCommand(ManageRequestInputsForm target, Errors errors) + { + } + + @Override + public boolean handlePost(ManageRequestInputsForm form, BindException errors) throws Exception + { + SpecimenRequestManager.SpecimenRequestInput[] inputs = new SpecimenRequestManager.SpecimenRequestInput[form.getTitle().length]; + for (int i = 0; i < form.getTitle().length; i++) + { + String title = form.getTitle()[i]; + String helpText = form.getHelpText()[i]; + inputs[i] = new SpecimenRequestManager.SpecimenRequestInput(title, helpText, i); + } + + if (form.getMultiline() != null) + { + for (int index : form.getMultiline()) + inputs[index].setMultiLine(true); + } + if (form.getRequired() != null) + { + for (int index : form.getRequired()) + inputs[index].setRequired(true); + } + if (form.getRememberSiteValue() != null) + { + for (int index : form.getRememberSiteValue()) + inputs[index].setRememberSiteValue(true); + } + SpecimenRequestManager.get().saveNewSpecimenRequestInputs(getContainer(), inputs); + return true; + } + + @Override + public ActionURL getSuccessURL(ManageRequestInputsForm manageRequestInputsForm) + { + return getManageStudyURL(); + } + } + + public static class RequirementForm extends IdForm + { + private int _requirementId; + + public int getRequirementId() + { + return _requirementId; + } + + public void setRequirementId(int requirementId) + { + _requirementId = requirementId; + } + } + + public static class ManageRequirementForm extends RequirementForm + { + private boolean _complete; + private String _comment; + private String[] _notificationIdPairs; + private boolean _emailInactiveUsers; + + public String getComment() + { + return _comment; + } + + public void setComment(String comment) + { + _comment = comment; + } + + public boolean isComplete() + { + return _complete; + } + + public void setComplete(boolean complete) + { + _complete = complete; + } + + public String[] getNotificationIdPairs() + { + return _notificationIdPairs; + } + + public void setNotificationIdPairs(String[] notificationIdPairs) + { + _notificationIdPairs = notificationIdPairs; + } + + public boolean isEmailInactiveUsers() + { + return _emailInactiveUsers; + } + + public void setEmailInactiveUsers(boolean emailInactiveUsers) + { + _emailInactiveUsers = emailInactiveUsers; + } + } + + @RequiresPermission(ManageRequestsPermission.class) + public class DeleteRequirementAction extends FormHandlerAction + { + @Override + public void validateCommand(RequirementForm target, Errors errors) + { + } + + @Override + public boolean handlePost(RequirementForm form, BindException errors) throws Exception + { + SpecimenRequestRequirement requirement = + SpecimenRequestRequirementProvider.get().getRequirement(getContainer(), form.getRequirementId()); + if (requirement.getRequestId() == form.getId()) + { + SpecimenRequestManager.get().deleteRequestRequirement(getUser(), requirement); + return true; + } + + return false; + } + + @Override + public ActionURL getSuccessURL(RequirementForm requirementForm) + { + return getManageRequestURL(requirementForm.getId(), null); + } + } + + public class ManageRequirementBean + { + private final GridView _historyView; + private final SpecimenRequestRequirement _requirement; + private final boolean _requestManager; + private final List _possibleNotifications; + private final boolean _finalState; + + public ManageRequirementBean(ViewContext context, SpecimenRequest request, SpecimenRequestRequirement requirement) + { + _requirement = requirement; + _possibleNotifications = SpecimenController.getPossibleNotifications(request); + SimpleFilter filter = new SimpleFilter(FieldKey.fromParts("RequestId"), requirement.getRequestId()); + filter.addCondition(FieldKey.fromParts("RequirementId"), requirement.getRowId()); + _requestManager = context.getContainer().hasPermission(context.getUser(), ManageRequestsPermission.class); + _historyView = getRequestEventGridView(context.getRequest(), null, filter); + _finalState = SpecimenRequestManager.get().isInFinalState(request); + } + + public boolean isDefaultNotification(ActorNotificationRecipientSet notification) + { + RequestNotificationSettings settings = SettingsManager.get().getRequestNotificationSettings(getContainer()); + if (settings.getDefaultEmailNotifyEnum() == RequestNotificationSettings.DefaultEmailNotifyEnum.All) + return true; // All should be checked + else if (settings.getDefaultEmailNotifyEnum() == RequestNotificationSettings.DefaultEmailNotifyEnum.None) + return false; // None should be checked + // Otherwise use Actor Notification + + Integer requirementActorId = _requirement.getActorId(); + Integer notificationActorId = notification.getActor() != null ? notification.getActor().getRowId() : null; + Integer requirementSiteId = _requirement.getSiteId(); + Integer notificationSiteId = notification.getLocation() != null ? notification.getLocation().getRowId() : null; + return nullSafeEqual(requirementActorId, notificationActorId) && + nullSafeEqual(requirementSiteId, notificationSiteId); + } + + public GridView getHistoryView() + { + return _historyView; + } + + public SpecimenRequestRequirement getRequirement() + { + return _requirement; + } + + public List getPossibleNotifications() + { + return _possibleNotifications; + } + + public boolean isRequestManager() + { + return _requestManager; + } + + public boolean isFinalState() + { + return _finalState; + } + } + + @RequiresPermission(ReadPermission.class) + public class ManageRequirementAction extends FormViewAction + { + private SpecimenRequest _specimenRequest; + + @Override + public void validateCommand(ManageRequirementForm target, Errors errors) + { + } + + @Override + public ModelAndView getView(ManageRequirementForm form, boolean reshow, BindException errors) + { + _specimenRequest = SpecimenRequestManager.get().getRequest(getContainer(), form.getId()); + final SpecimenRequestRequirement requirement = + SpecimenRequestRequirementProvider.get().getRequirement(getContainer(), form.getRequirementId()); + if (_specimenRequest == null || requirement == null || requirement.getRequestId() != form.getId()) + throw new NotFoundException(); + + return new JspView<>("/org/labkey/specimen/view/manageRequirement.jsp", + new ManageRequirementBean(getViewContext(), _specimenRequest, requirement), errors); + } + + @Override + public boolean handlePost(final ManageRequirementForm form, BindException errors) throws Exception + { + if (!getContainer().hasPermission(getUser(), ManageRequestsPermission.class)) + throw new UnauthorizedException("You do not have permission to update requirements!"); + + _specimenRequest = SpecimenRequestManager.get().getRequest(getContainer(), form.getId()); + final SpecimenRequestRequirement requirement = + SpecimenRequestRequirementProvider.get().getRequirement(getContainer(), form.getRequirementId()); + if (_specimenRequest == null || requirement == null || requirement.getRequestId() != form.getId()) + throw new NotFoundException(); + + List files = getAttachmentFileList(); + RequestEventType eventType; + StringBuilder comment = new StringBuilder(); + comment.append(requirement.getRequirementSummary()); + String eventSummary; + if (form.isComplete() != requirement.isComplete()) + { + SpecimenRequestRequirement clone = requirement.createMutable(); + clone.setComplete(form.isComplete()); + SpecimenRequestManager.get().updateRequestRequirement(getUser(), clone); + eventType = RequestEventType.REQUEST_STATUS_CHANGED; + comment.append("\nStatus changed to ").append(form.isComplete() ? "complete" : "incomplete"); + eventSummary = comment.toString(); + } + else + { + eventType = RequestEventType.COMMENT_ADDED; + eventSummary = "Comment added."; + } + + if (form.getComment() != null && form.getComment().length() > 0) + comment.append("\n").append(form.getComment()); + + SpecimenRequestEvent event; + try + { + event = SpecimenRequestManager.get().createRequestEvent(getUser(), requirement, eventType, comment.toString(), files); + } + catch (Exception e) + { + errors.reject(ERROR_MSG, "The request could not be updated because of an unexpected error. " + + "Please report this problem to an administrator. Error details: " + e.getMessage()); + return false; + } + try + { + + List recipients = getNotifications(_specimenRequest, form.getNotificationIdPairs()); + DefaultRequestNotification notification = new DefaultRequestNotification(_specimenRequest, recipients, + eventSummary, event, form.getComment(), requirement, getViewContext()); + sendNotification(notification, form.isEmailInactiveUsers(), errors); + } + catch (ConfigurationException | IOException e) + { + errors.reject(ERROR_MSG, "The request was updated successfully, but the notification failed: " + e.getMessage()); + return false; + } + + return !errors.hasErrors(); + } + + @Override + public ActionURL getSuccessURL(ManageRequirementForm manageRequirementForm) + { + return getManageRequestURL(_specimenRequest.getRowId(), null); + } + + @Override + public void addNavTrail(NavTree root) + { + addSpecimenRequestNavTrail(root, _specimenRequest.getRowId()); + root.addChild("Manage Requirement"); + } + } + + @RequiresPermission(ManageRequestsPermission.class) + public class DeleteMissingRequestSpecimensAction extends FormHandlerAction + { + @Override + public void validateCommand(IdForm target, Errors errors) + { + } + + @Override + public boolean handlePost(IdForm form, BindException errors) throws Exception + { + SpecimenRequest request = SpecimenRequestManager.get().getRequest(getContainer(), form.getId()); + if (request == null) + throw new NotFoundException("Specimen request " + form.getId() + " does not exist."); + + SpecimenRequestManager.get().deleteMissingSpecimens(request); + return true; + } + + @Override + public ActionURL getSuccessURL(IdForm requirementForm) + { + return getManageRequestURL(requirementForm.getId(), null); + } + } + + public static class LabSpecimenListsBean + { + public enum Type + { + PROVIDING("Providing"), + ORIGINATING("Originating"); + + private final String _display; + + Type(String display) + { + _display = display; + } + + public String getDisplay() + { + return _display; + } + } + + private final SpecimenRequest _specimenRequest; + private final Type _type; + + private Map> _specimensBySiteId; + private List _possibleNotifications; + private boolean _requirementsComplete; + + public LabSpecimenListsBean(SpecimenRequest specimenRequest, LabSpecimenListsBean.Type type) + { + _specimenRequest = specimenRequest; + _type = type; + _requirementsComplete = true; + for (int i = 0; i < specimenRequest.getRequirements().length && _requirementsComplete; i++) + { + SpecimenRequestRequirement requirement = specimenRequest.getRequirements()[i]; + _requirementsComplete = requirement.isComplete(); + } + } + + public SpecimenRequest getSpecimenRequest() + { + return _specimenRequest; + } + + private synchronized Map> getSpecimensBySiteId() + { + if (_specimensBySiteId == null) + { + _specimensBySiteId = new HashMap<>(); + List vials = _specimenRequest.getVials(); + for (Vial vial : vials) + { + LocationImpl location; + if (_type == LabSpecimenListsBean.Type.ORIGINATING) + location = LocationManager.get().getOriginatingLocation(vial); + else + location = LocationManager.get().getCurrentLocation(vial); + if (location != null) + { + List current = _specimensBySiteId.computeIfAbsent(location.getRowId(), k -> new ArrayList<>()); + current.add(vial); + } + } + } + return _specimensBySiteId; + } + + public synchronized List getPossibleNotifications() + { + if (_possibleNotifications == null) + _possibleNotifications = SpecimenController.getPossibleNotifications(_specimenRequest); + return _possibleNotifications; + } + + public Set getLabs() + { + Map> siteIdToSpecimens = getSpecimensBySiteId(); + Set locations = new HashSet<>(siteIdToSpecimens.size()); + for (Integer locationId : siteIdToSpecimens.keySet()) + locations.add(LocationManager.get().getLocation(_specimenRequest.getContainer(), locationId)); + return locations; + } + + public List getSpecimens(LocationImpl location) + { + Map> siteSpecimenLists = getSpecimensBySiteId(); + return siteSpecimenLists.get(location.getRowId()); + } + + public Type getType() + { + return _type; + } + + public boolean isRequirementsComplete() + { + return _requirementsComplete; + } + } + + @RequiresPermission(ManageRequestsPermission.class) + public class LabSpecimenListsAction extends SimpleViewAction + { + private int _requestId; + private LabSpecimenListsBean.Type _type; + + @Override + public ModelAndView getView(LabSpecimenListsForm form, BindException errors) + { + SpecimenRequest request = SpecimenRequestManager.get().getRequest(getContainer(), form.getId()); + if (request == null || form.getListType() == null) + throw new NotFoundException(); + + _requestId = request.getRowId(); + + try + { + _type = LabSpecimenListsBean.Type.valueOf(form.getListType()); + } + catch (IllegalArgumentException e) + { + // catch malformed/old URL case, where the posted value of 'type' isn't a valid Type: + throw new NotFoundException("Unrecognized list type."); + } + return new JspView<>("/org/labkey/specimen/view/labSpecimenLists.jsp", new LabSpecimenListsBean(request, _type)); + } + + @Override + public void addNavTrail(NavTree root) + { + addSpecimenRequestNavTrail(root, _requestId); + root.addChild(_type.getDisplay() + " Lab Vial Lists"); + } + } + + public static class LabSpecimenListsForm extends IdForm + { + private String _listType; + + public String getListType() + { + return _listType; + } + + public void setListType(String listType) + { + _listType = listType; + } + } + + public static class ExportSiteForm extends IdForm + { + private String _export; + private String _specimenIds; + private String _listType; + private int _sourceSiteId; + private int _destSiteId; + + public String getExport() + { + return _export; + } + + public void setExport(String export) + { + _export = export; + } + + public String getSpecimenIds() + { + return _specimenIds; + } + + public void setSpecimenIds(String specimenIds) + { + _specimenIds = specimenIds; + } + + public int getDestSiteId() + { + return _destSiteId; + } + + public void setDestSiteId(int destSiteId) + { + _destSiteId = destSiteId; + } + + public int getSourceSiteId() + { + return _sourceSiteId; + } + + public void setSourceSiteId(int sourceSiteId) + { + _sourceSiteId = sourceSiteId; + } + + public String getListType() + { + return _listType; + } + + public void setListType(String listType) + { + _listType = listType; + } + } + + @RequiresPermission(ManageRequestsPermission.class) + public class DownloadSpecimenListAction extends SimpleViewAction + { + @Override + public ModelAndView getView(ExportSiteForm form, BindException errors) throws Exception + { + SpecimenRequest specimenRequest = SpecimenRequestManager.get().getRequest(getContainer(), form.getId()); + LocationImpl sourceLocation = LocationManager.get().getLocation(getContainer(), form.getSourceSiteId()); + LocationImpl destLocation = LocationManager.get().getLocation(getContainer(), form.getDestSiteId()); + if (specimenRequest == null || sourceLocation == null || destLocation == null) + throw new NotFoundException(); + + LabSpecimenListsBean.Type type = LabSpecimenListsBean.Type.valueOf(form.getListType()); + if (null != form.getExport()) + { + if (EXPORT_TSV.equals(form.getExport())) + { + try (TSVGridWriter writer = getSpecimenListTsvWriter(specimenRequest, sourceLocation, destLocation, type)) + { + writer.write(getViewContext().getResponse()); + } + } + else if (EXPORT_XLS.equals(form.getExport())) + { + try (ExcelWriter writer = getSpecimenListXlsWriter(specimenRequest, sourceLocation, destLocation, type)) + { + writer.write(getViewContext().getResponse()); + } + } + } + return null; + } + + @Override + public void addNavTrail(NavTree root) + { + throw new UnsupportedOperationException("Not Yet Implemented"); + } + } + + private static final String EXPORT_TSV = "tsv"; + private static final String EXPORT_XLS = "xls"; + + public static class EmailSpecimenListForm extends IdForm + { + private boolean _sendXls; + private boolean _sendTsv; + private String _comments; + private String[] _notify; + private String _listType; + private boolean _emailInactiveUsers; + + public String getComments() + { + return _comments; + } + + public void setComments(String comments) + { + _comments = comments; + } + + public String[] getNotify() + { + return _notify; + } + + public void setNotify(String[] notify) + { + _notify = notify; + } + + public boolean isSendTsv() + { + return _sendTsv; + } + + public void setSendTsv(boolean sendTsv) + { + _sendTsv = sendTsv; + } + + public boolean isSendXls() + { + return _sendXls; + } + + public void setSendXls(boolean sendXls) + { + _sendXls = sendXls; + } + + public String getListType() + { + return _listType; + } + + public void setListType(String listType) + { + _listType = listType; + } + + public boolean isEmailInactiveUsers() + { + return _emailInactiveUsers; + } + + public void setEmailInactiveUsers(boolean emailInactiveUsers) + { + _emailInactiveUsers = emailInactiveUsers; + } + } + + @RequiresPermission(ManageRequestsPermission.class) + public class EmailLabSpecimenListsAction extends FormHandlerAction + { + @Override + public void validateCommand(EmailSpecimenListForm target, Errors errors) + { + } + + @Override + public boolean handlePost(EmailSpecimenListForm form, BindException errors) throws Exception + { + final SpecimenRequest request = SpecimenRequestManager.get().getRequest(getContainer(), form.getId()); + if (request == null) + throw new NotFoundException(); + + LocationImpl receivingLocation = LocationManager.get().getLocation(getContainer(), request.getDestinationSiteId()); + if (receivingLocation == null) + throw new NotFoundException(); + + final LabSpecimenListsBean.Type type; + try + { + type = LabSpecimenListsBean.Type.valueOf(form.getListType()); + } + catch (IllegalArgumentException e) + { + throw new NotFoundException(); + } + + Map> notifications = new HashMap<>(); + if (form.getNotify() != null) + { + for (String tuple : form.getNotify()) + { + String[] idStrs = tuple.split(","); + if (idStrs.length != 3) + throw new IllegalStateException("Expected triple."); + int[] ids = new int[3]; + for (int i = 0; i < 3; i++) + ids[i] = Integer.parseInt(idStrs[i]); + LocationImpl originatingOrProvidingLocation = LocationManager.get().getLocation(getContainer(), ids[0]); + SpecimenRequestActor notifyActor = SpecimenRequestRequirementProvider.get().getActor(getContainer(), ids[1]); + LocationImpl notifyLocation = null; + if (notifyActor.isPerSite() && ids[2] >= 0) + notifyLocation = LocationManager.get().getLocation(getContainer(), ids[2]); + List emailRecipients = notifications.computeIfAbsent(originatingOrProvidingLocation, k -> new ArrayList<>()); + emailRecipients.add(new ActorNotificationRecipientSet(notifyActor, notifyLocation)); + } + + for (final LocationImpl originatingOrProvidingLocation : notifications.keySet()) + { + List formFiles = getAttachmentFileList(); + if (form.isSendTsv()) + { + try (TSVGridWriter tsvWriter = getSpecimenListTsvWriter(request, originatingOrProvidingLocation, receivingLocation, type)) + { + StringBuilder tsvBuilder = new StringBuilder(); + tsvWriter.write(tsvBuilder); + formFiles.add(new ByteArrayAttachmentFile(tsvWriter.getFilenamePrefix() + ".tsv", tsvBuilder.toString().getBytes(StandardCharsets.UTF_8), TSVWriter.DELIM.TAB.contentType)); + } + } + + if (form.isSendXls()) + { + try (ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); OutputStream ostream = new BufferedOutputStream(byteStream); ExcelWriter xlsWriter = getSpecimenListXlsWriter(request, originatingOrProvidingLocation, receivingLocation, type)) + { + xlsWriter.write(ostream); + ostream.flush(); + formFiles.add(new ByteArrayAttachmentFile(xlsWriter.getFilenamePrefix() + "." + xlsWriter.getDocumentType().name(), byteStream.toByteArray(), xlsWriter.getDocumentType().getMimeType())); + } + } + + final StringBuilder content = new StringBuilder(); + if (form.getComments() != null) + content.append(form.getComments()); + + String header = type.getDisplay() + " location notification of specimen shipment to " + receivingLocation.getDisplayName(); + try + { + SpecimenRequestEvent event = SpecimenRequestManager.get().createRequestEvent(getUser(), request, + RequestEventType.SPECIMEN_LIST_GENERATED, header + "\n" + content, formFiles); + + final Container container = getContainer(); + final User user = getUser(); + List emailRecipients = notifications.get(originatingOrProvidingLocation); + DefaultRequestNotification notification = new DefaultRequestNotification(request, emailRecipients, + header, event, content.toString(), null, getViewContext()) + { + @Override + protected List getSpecimenList() + { + SimpleFilter filter = getSpecimenListFilter(getSpecimenRequest(), originatingOrProvidingLocation, type); + return SpecimenManagerNew.get().getVials(container, user, filter); +// return new TableSelector(StudySchema.getInstance().getTableInfoSpecimenDetail(container), filter, null).getArrayList(Specimen.class); + } + + }; + sendNotification(notification, form.isEmailInactiveUsers(), errors); + } + catch (ConfigurationException | IOException e) + { + errors.reject(ERROR_MSG, e.getMessage()); + } + + if (errors.hasErrors()) + break; + } + } + + return !errors.hasErrors(); + } + + @Override + public ActionURL getSuccessURL(EmailSpecimenListForm emailSpecimenListForm) + { + return getManageRequestURL(emailSpecimenListForm.getId(), null); + } + } + + public static class EnabledSpecimenImportForm + { + private String _activeTransform; + + public String getActiveTransform() + { + return _activeTransform; + } + + @SuppressWarnings("unused") + public void setActiveTransform(String activeTransform) + { + _activeTransform = activeTransform; + } + } + + @RequiresPermission(AdminPermission.class) + public class ChooseImporterAction extends FormViewAction + { + @Override + public void addNavTrail(NavTree root) + { + root.addChild("Specimen Import Mechanism"); + } + + @Override + public void validateCommand(EnabledSpecimenImportForm target, Errors errors) + { + } + + @Override + public ModelAndView getView(EnabledSpecimenImportForm form, boolean reshow, BindException errors) throws Exception + { + setHelpTopic(new HelpTopic("externalSpecimens")); + return new JspView<>("/org/labkey/specimen/view/chooseImporter.jsp", form, errors); + } + + @Override + public boolean handlePost(EnabledSpecimenImportForm form, BindException errors) throws Exception + { + PropertyManager.PropertyMap props = PropertyManager.getWritableProperties(getContainer(), "enabledSpecimenImporter", true); + props.put("active", form.getActiveTransform()); + props.save(); + return true; + } + + @Override + public URLHelper getSuccessURL(EnabledSpecimenImportForm configForm) + { + Container c = getContainer(); + SpecimenService specimenService = SpecimenService.get(); + + String active = specimenService.getActiveSpecimenImporter(c); + SpecimenTransform activeTransform = specimenService.getSpecimenTransform(active); + return activeTransform.getManageAction(c, getUser()); + } + } + + public static class ManageLocationTypesForm + { + private boolean _repository; + private boolean _clinic; + private boolean _sal; + private boolean _endpoint; + + public boolean isRepository() + { + return _repository; + } + + public void setRepository(boolean repository) + { + _repository = repository; + } + + public boolean isClinic() + { + return _clinic; + } + + public void setClinic(boolean clinic) + { + _clinic = clinic; + } + + public boolean isSal() + { + return _sal; + } + + public void setSal(boolean sal) + { + _sal = sal; + } + + public boolean isEndpoint() + { + return _endpoint; + } + + public void setEndpoint(boolean endpoint) + { + _endpoint = endpoint; + } + } + + @RequiresPermission(AdminPermission.class) + public class ManageLocationTypesAction extends SimpleViewAction + { + @Override + public ModelAndView getView(ManageLocationTypesForm form, BindException errors) + { + Study study = getStudyRedirectIfNull(); + form.setRepository(study.isAllowReqLocRepository()); + form.setClinic(study.isAllowReqLocClinic()); + form.setSal(study.isAllowReqLocSal()); + form.setEndpoint(study.isAllowReqLocEndpoint()); + return new JspView<>("/org/labkey/specimen/view/manageLocationTypes.jsp", form); + } + + @Override + public void addNavTrail(NavTree root) + { + setHelpTopic("manageLocations"); + addManageStudyNavTrail(root); + root.addChild("Manage Location Types"); + } + } + + @RequiresPermission(AdminPermission.class) + public class SaveLocationsTypeSettingsAction extends MutatingApiAction + { + @Override + public ApiResponse execute(ManageLocationTypesForm form, BindException errors) + { + ApiSimpleResponse response = new ApiSimpleResponse(); + Study study = getStudyThrowIfNull(); + StudyService.get().saveLocationSettings(study, getUser(), form.isRepository(), form.isClinic(), form.isSal(), form.isEndpoint()); + response.put("success", true); + return response; + } + } + public static class ManageCommentsForm + { + private Integer _participantCommentDatasetId; + private String _participantCommentProperty; + private Integer _participantVisitCommentDatasetId; + private String _participantVisitCommentProperty; + private boolean _reshow; + + public Integer getParticipantCommentDatasetId() + { + return _participantCommentDatasetId; + } + + public void setParticipantCommentDatasetId(Integer participantCommentDatasetId) + { + _participantCommentDatasetId = participantCommentDatasetId; + } + + public String getParticipantCommentProperty() + { + return _participantCommentProperty; + } + + public void setParticipantCommentProperty(String participantCommentProperty) + { + _participantCommentProperty = participantCommentProperty; + } + + public Integer getParticipantVisitCommentDatasetId() + { + return _participantVisitCommentDatasetId; + } + + public void setParticipantVisitCommentDatasetId(Integer participantVisitCommentDatasetId) + { + _participantVisitCommentDatasetId = participantVisitCommentDatasetId; + } + + public String getParticipantVisitCommentProperty() + { + return _participantVisitCommentProperty; + } + + public void setParticipantVisitCommentProperty(String participantVisitCommentProperty) + { + _participantVisitCommentProperty = participantVisitCommentProperty; + } + + public boolean isReshow() + { + return _reshow; + } + + public void setReshow(boolean reshow) + { + _reshow = reshow; + } + } + + @RequiresPermission(AdminPermission.class) + public class ManageSpecimenCommentsAction extends FormViewAction + { + @Override + public void validateCommand(ManageCommentsForm form, Errors errors) + { + String subjectNoun = StudyService.get().getSubjectNounSingular(getContainer()); + final Study study = getStudyRedirectIfNull(getContainer()); + if (form.getParticipantCommentDatasetId() != null && form.getParticipantCommentDatasetId() != -1) + { + Dataset ds = StudyService.get().getDataset(getContainer(), form.getParticipantCommentDatasetId()); + if (ds != null && !ds.isDemographicData()) + { + errors.reject(ERROR_MSG, "The Dataset specified to contain " + subjectNoun + " comments must be a demographics dataset."); + } + + if (form.getParticipantCommentProperty() == null) + errors.reject(ERROR_MSG, "A Comment field name must be specified for the " + subjectNoun + " Comment Assignment."); + } + + if (study.getTimepointType() != TimepointType.CONTINUOUS) + { + if (form.getParticipantVisitCommentDatasetId() != null && form.getParticipantVisitCommentDatasetId() != -1) + { + Dataset ds = StudyService.get().getDataset(getContainer(), form.getParticipantVisitCommentDatasetId()); + if (ds != null && ds.isDemographicData()) + { + errors.reject(ERROR_MSG, "The Dataset specified to contain " + subjectNoun + "/Visit comments cannot be a demographics dataset."); + } + + if (form.getParticipantVisitCommentProperty() == null) + errors.reject(ERROR_MSG, "A Comment field name must be specified for the " + subjectNoun + "/Visit Comment Assignment."); + } + } + } + + @Override + public ModelAndView getView(ManageCommentsForm form, boolean reshow, BindException errors) + { + Study study = getStudyRedirectIfNull(); + StudyInternalService svc = StudyInternalService.get(); + + if (!svc.hasEditableDatasets(study)) + return new HtmlView("Comments can only be configured for studies with editable datasets."); + + if (!form.isReshow()) + { + form.setParticipantCommentDatasetId(svc.getParticipantCommentDatasetId(study)); + form.setParticipantCommentProperty(svc.getParticipantCommentProperty(study)); + + if (study.getTimepointType() != TimepointType.CONTINUOUS) + { + form.setParticipantVisitCommentDatasetId(svc.getParticipantVisitCommentDatasetId(study)); + form.setParticipantVisitCommentProperty(svc.getParticipantVisitCommentProperty(study)); + } + } + JspView view = new JspView<>("/org/labkey/specimen/view/manageComments.jsp", form, errors); + view.setTitle("Comment Configuration"); + + return view; + } + + @Override + public boolean handlePost(ManageCommentsForm form, BindException errors) + { + StudyInternalService.get().saveCommentsSettings(getStudyThrowIfNull(), getUser(), form.getParticipantCommentDatasetId(), form.getParticipantCommentProperty(), + form.getParticipantVisitCommentDatasetId(), form.getParticipantVisitCommentProperty()); + return true; + } + + @Override + public ActionURL getSuccessURL(ManageCommentsForm form) + { + return getManageStudyURL(); + } + + @Override + public void addNavTrail(NavTree root) + { + setHelpTopic("manageComments"); + addManageStudyNavTrail(root); + root.addChild("Manage Comments"); + } + } + + @RequiresPermission(ReadPermission.class) + public class SelectedSpecimensAction extends QueryViewAction + { + private boolean _vialView; + private Collection _filterPds = null; + + public SelectedSpecimensAction() + { + super(SpecimenViewTypeForm.class); + } + + @Override + protected ModelAndView getHtmlView(SpecimenViewTypeForm form, BindException errors) throws Exception + { + Study study = getStudyRedirectIfNull(); + Set> ptidVisits = new HashSet<>(); + for (ParticipantDataset pd : getFilterPds()) + { + if (pd.getSequenceNum() == null) + { + ptidVisits.add(new Pair<>(pd.getParticipantId(), null)); + } + else if (study.getTimepointType() != TimepointType.VISIT && pd.getVisitDate() != null) + { + ptidVisits.add(new Pair<>(pd.getParticipantId(), DateUtil.formatDate(pd.getContainer(), pd.getVisitDate()))); + } + else + { + Visit visit = pd.getSequenceNum() != null ? UpdateSpecimenCommentsBean.getVisitForSequence(study, pd.getSequenceNum()) : null; + ptidVisits.add(new Pair<>(pd.getParticipantId(), visit != null ? visit.getLabel() : "" + StudyInternalService.get().formatSequenceNum(pd.getSequenceNum()))); + } + } + SpecimenQueryView view = createInitializedQueryView(form, errors, form.getExportType() != null, null); + JspView header = new JspView<>("/org/labkey/specimen/view/specimenHeader.jsp", + new SpecimenHeaderBean(getViewContext(), view, ptidVisits)); + return new VBox(header, view); + } + + private @NotNull Collection getFilterPds() + { + if (_filterPds == null) + { + Set lsids = getSelectionLsids(); + _filterPds = StudyInternalService.get().getParticipantDatasets(getContainer(), lsids); + } + return _filterPds; + } + + @Override + protected SpecimenQueryView createQueryView(SpecimenViewTypeForm form, BindException errors, boolean forExport, String dataRegion) + { + _vialView = form.isShowVials(); + Set lsids = getSelectionLsids(); + SpecimenQueryView view; + CohortFilter cohortFilter = CohortService.get().getFromURL(getContainer(), getUser(), getViewContext().getActionURL(), SpecimenQueryView.ViewType.SUMMARY.getQueryName()); + if (lsids != null) + { + view = getSpecimenQueryView(form.isShowVials(), forExport, getFilterPds(), form.getViewModeEnum(), cohortFilter); + } + else + view = getSpecimenQueryView(form.isShowVials(), forExport, null, form.getViewModeEnum(), cohortFilter); + view.setAllowExportExternalQuery(false); + return view; + } + + @Override + public void addNavTrail(NavTree root) + { + addRootNavTrail(root); + root.addChild(_vialView ? "Selected Vials" : "Selected Specimens"); + } + } + +/* + // Used for testing + @RequiresSiteAdmin + public class DropVialIndices extends SimpleRedirectAction + { + @Override + public URLHelper getRedirectURL(Object o) throws Exception + { + new SpecimenTablesProvider(getContainer(), getUser(), null).dropTableIndices(SpecimenTablesProvider.VIAL_TABLENAME); + return new ActionURL(BeginAction.class, getContainer()); + } + } + + // Used for testing + @RequiresSiteAdmin + public class AddVialIndices extends SimpleRedirectAction + { + @Override + public URLHelper getRedirectURL(Object o) throws Exception + { + new SpecimenTablesProvider(getContainer(), getUser(), null).addTableIndices(SpecimenTablesProvider.VIAL_TABLENAME); + return new ActionURL(BeginAction.class, getContainer()); + } + } +*/ +} diff --git a/specimen/src/org/labkey/specimen/actions/SpecimenController2.java b/specimen/src/org/labkey/specimen/actions/SpecimenController2.java deleted file mode 100644 index 104952fd..00000000 --- a/specimen/src/org/labkey/specimen/actions/SpecimenController2.java +++ /dev/null @@ -1,2092 +0,0 @@ -package org.labkey.specimen.actions; - -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.json.JSONObject; -import org.labkey.api.action.ApiResponse; -import org.labkey.api.action.ApiSimpleResponse; -import org.labkey.api.action.FormHandlerAction; -import org.labkey.api.action.FormViewAction; -import org.labkey.api.action.HasViewContext; -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.attachments.AttachmentParent; -import org.labkey.api.attachments.BaseDownloadAction; -import org.labkey.api.data.ActionButton; -import org.labkey.api.data.BeanViewForm; -import org.labkey.api.data.Container; -import org.labkey.api.data.DataRegion; -import org.labkey.api.data.DataRegionSelection; -import org.labkey.api.data.DbScope; -import org.labkey.api.data.ExcelColumn; -import org.labkey.api.data.SimpleFilter; -import org.labkey.api.module.FolderType; -import org.labkey.api.pipeline.PipeRoot; -import org.labkey.api.pipeline.PipelineService; -import org.labkey.api.pipeline.PipelineStatusUrls; -import org.labkey.api.pipeline.browse.PipelinePathForm; -import org.labkey.api.query.ValidationException; -import org.labkey.api.security.RequiresPermission; -import org.labkey.api.security.RequiresSiteAdmin; -import org.labkey.api.security.User; -import org.labkey.api.security.ValidEmail; -import org.labkey.api.security.permissions.AdminPermission; -import org.labkey.api.security.permissions.ReadPermission; -import org.labkey.api.specimen.AmbiguousLocationException; -import org.labkey.api.specimen.RequestedSpecimens; -import org.labkey.api.specimen.SpecimenManager; -import org.labkey.api.specimen.SpecimenManagerNew; -import org.labkey.api.specimen.SpecimenRequestException; -import org.labkey.api.specimen.SpecimenRequestManager; -import org.labkey.api.specimen.SpecimenSchema; -import org.labkey.api.specimen.Vial; -import org.labkey.api.specimen.actions.HiddenFormInputGenerator; -import org.labkey.api.specimen.actions.IdForm; -import org.labkey.api.specimen.actions.SelectSpecimenProviderBean; -import org.labkey.api.specimen.actions.SpecimenEventBean; -import org.labkey.api.specimen.importer.RequestabilityManager; -import org.labkey.api.specimen.importer.SimpleSpecimenImporter; -import org.labkey.api.specimen.location.LocationManager; -import org.labkey.api.specimen.model.ExtendedSpecimenRequestView; -import org.labkey.api.specimen.model.SpecimenRequestEvent; -import org.labkey.api.specimen.query.SpecimenQueryView; -import org.labkey.api.specimen.requirements.SpecimenRequest; -import org.labkey.api.specimen.requirements.SpecimenRequestRequirement; -import org.labkey.api.specimen.requirements.SpecimenRequestRequirementProvider; -import org.labkey.api.specimen.requirements.SpecimenRequestRequirementType; -import org.labkey.api.specimen.security.permissions.ManageRequestSettingsPermission; -import org.labkey.api.specimen.security.permissions.RequestSpecimensPermission; -import org.labkey.api.specimen.settings.DisplaySettings; -import org.labkey.api.specimen.settings.RepositorySettings; -import org.labkey.api.specimen.settings.RequestNotificationSettings; -import org.labkey.api.specimen.settings.SettingsManager; -import org.labkey.api.specimen.view.SpecimenWebPart; -import org.labkey.api.study.MapArrayExcelWriter; -import org.labkey.api.study.SpecimenService; -import org.labkey.api.study.SpecimenUrls; -import org.labkey.api.study.Study; -import org.labkey.api.study.StudyInternalService; -import org.labkey.api.study.StudyService; -import org.labkey.api.study.StudyUrls; -import org.labkey.api.study.security.permissions.ManageStudyPermission; -import org.labkey.api.util.ConfigurationException; -import org.labkey.api.util.ExceptionUtil; -import org.labkey.api.util.GUID; -import org.labkey.api.util.HelpTopic; -import org.labkey.api.util.PageFlowUtil; -import org.labkey.api.util.Pair; -import org.labkey.api.util.URLHelper; -import org.labkey.api.view.ActionURL; -import org.labkey.api.view.AjaxCompletion; -import org.labkey.api.view.HtmlView; -import org.labkey.api.view.JspView; -import org.labkey.api.view.NavTree; -import org.labkey.api.view.NotFoundException; -import org.labkey.api.view.RedirectException; -import org.labkey.api.view.VBox; -import org.labkey.api.view.ViewBackgroundInfo; -import org.labkey.api.view.ViewContext; -import org.labkey.api.view.WebPartView; -import org.labkey.specimen.pipeline.SpecimenArchive; -import org.labkey.specimen.pipeline.SpecimenBatch; -import org.labkey.specimen.query.SpecimenEventQueryView; -import org.labkey.specimen.query.SpecimenRequestQueryView; -import org.labkey.specimen.security.permissions.ManageDisplaySettingsPermission; -import org.labkey.specimen.security.permissions.ManageNotificationsPermission; -import org.labkey.specimen.security.permissions.ManageRequestRequirementsPermission; -import org.labkey.specimen.view.SpecimenSearchWebPart; -import org.springframework.validation.BindException; -import org.springframework.validation.Errors; -import org.springframework.web.servlet.ModelAndView; - -import javax.servlet.http.HttpServletRequest; -import java.io.File; -import java.io.IOException; -import java.sql.SQLException; -import java.sql.Timestamp; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -// TEMPORARY: Move specimen actions from study SpecimenController to here. Once all actions are moved, we'll rename this. -public class SpecimenController2 extends SpringActionController -{ - private static final DefaultActionResolver _resolver = new DefaultActionResolver( - SpecimenController2.class, - - ShowGroupMembersAction.class, - ShowSearchAction.class, - ShowUploadSpecimensAction.class, - ShowUploadSpecimensAction.ImportCompleteAction.class, - - // Report actions from SpecimenReportActions - SpecimenReportActions.ParticipantSummaryReportAction.class, - SpecimenReportActions.ParticipantTypeReportAction.class, - SpecimenReportActions.ParticipantSiteReportAction.class, - SpecimenReportActions.RequestReportAction.class, - SpecimenReportActions.RequestEnrollmentSiteReportAction.class, - SpecimenReportActions.RequestSiteReportAction.class, - SpecimenReportActions.RequestParticipantReportAction.class, - SpecimenReportActions.TypeParticipantReportAction.class, - SpecimenReportActions.TypeSummaryReportAction.class, - SpecimenReportActions.TypeCohortReportAction.class - ); - - private Study _study = null; - - public SpecimenController2() - { - setActionResolver(_resolver); - } - - @Nullable - public Study getStudy() - { - if (null == _study) - _study = StudyService.get().getStudy(getContainer()); - return _study; - } - - @NotNull - public Study getStudyThrowIfNull() throws IllegalStateException - { - Study study = StudyService.get().getStudy(getContainer()); - if (null == study) - { - // We expected to find a study - throw new NotFoundException("No study found."); - } - return study; - } - - @NotNull - public Study getStudyRedirectIfNull() - { - Study study = StudyService.get().getStudy(getContainer()); - if (null == study) - { - // redirect to the study home page, where admins will see a 'create study' button, - // and non-admins will simply see a message that no study exists. - throw new RedirectException(urlProvider(StudyUrls.class).getBeginURL(getContainer())); - } - return study; - } - - public void addRootNavTrail(NavTree root) - { - Study study = getStudyRedirectIfNull(); - Container c = getContainer(); - ActionURL rootURL; - FolderType folderType = c.getFolderType(); - if ("study".equals(folderType.getDefaultModule().getName())) - { - rootURL = folderType.getStartURL(c, getUser()); - } - else - { - rootURL = urlProvider(StudyUrls.class).getBeginURL(c); - } - root.addChild(study.getLabel(), rootURL); - } - - private void addBaseSpecimenNavTrail(NavTree root) - { - addRootNavTrail(root); - ActionURL overviewURL = new ActionURL(OverviewAction.class, getContainer()); - root.addChild("Specimen Overview", overviewURL); - } - - private void addSpecimenRequestsNavTrail(NavTree root) - { - addBaseSpecimenNavTrail(root); - root.addChild("Specimen Requests", new ActionURL(ViewRequestsAction.class, getContainer())); - } - - private void addManageStudyNavTrail(NavTree root) - { - urlProvider(StudyUrls.class).addManageStudyNavTrail(root, getContainer(), getUser()); - } - - private ActionURL getManageStudyURL() - { - return urlProvider(StudyUrls.class).getManageStudyURL(getContainer()); - } - - public void ensureSpecimenRequestsConfigured(boolean checkExistingStatuses) - { - if (!SettingsManager.get().isSpecimenRequestEnabled(getContainer(), checkExistingStatuses)) - throw new RedirectException(new ActionURL(SpecimenRequestConfigRequiredAction.class, getContainer())); - } - - @RequiresPermission(ReadPermission.class) - public class OverviewAction extends SimpleViewAction - { - @Override - public ModelAndView getView(Object form, BindException errors) - { - if (null == StudyService.get().getStudy(getContainer())) - return new HtmlView("This folder does not contain a study."); - SpecimenSearchWebPart specimenSearch = new SpecimenSearchWebPart(true); - SpecimenWebPart specimenSummary = new SpecimenWebPart(true, StudyService.get().getStudy(getContainer())); - return new VBox(specimenSummary, specimenSearch); - } - - @Override - public void addNavTrail(NavTree root) - { - addBaseSpecimenNavTrail(root); - } - } - - public static class SpecimenWebPartForm - { - private String[] _grouping1; - private String[] _grouping2; - private String[] _columns; - - public String[] getGrouping1() - { - return _grouping1; - } - - public void setGrouping1(String[] grouping1) - { - _grouping1 = grouping1; - } - - public String[] getGrouping2() - { - return _grouping2; - } - - public void setGrouping2(String[] grouping2) - { - _grouping2 = grouping2; - } - - public String[] getColumns() - { - return _columns; - } - - public void setColumns(String[] columns) - { - _columns = columns; - } - } - - @RequiresPermission(ManageDisplaySettingsPermission.class) - public static class ManageSpecimenWebPartAction extends SimpleViewAction - { - @Override - public ModelAndView getView(SpecimenWebPartForm form, BindException errors) - { - RepositorySettings settings = SettingsManager.get().getRepositorySettings(getContainer()); - ArrayList groupings = settings.getSpecimenWebPartGroupings(); - form.setGrouping1(groupings.get(0)); - form.setGrouping2(groupings.get(1)); - form.setColumns(SpecimenRequestManager.get().getGroupedValueAllowedColumns()); - return new JspView<>("/org/labkey/specimen/view/manageSpecimenWebPart.jsp", form); - } - - @Override - public void addNavTrail(NavTree root) - { - setHelpTopic("manageSpecimens#group"); - urlProvider(StudyUrls.class).addManageStudyNavTrail(root, getContainer(), getUser()); - root.addChild("Configure Specimen Web Part"); - } - } - - @RequiresPermission(AdminPermission.class) - public class SaveSpecimenWebPartSettingsAction extends MutatingApiAction - { - @Override - public ApiResponse execute(SpecimenWebPartForm form, BindException errors) - { - ApiSimpleResponse response = new ApiSimpleResponse(); - Study study = getStudy(); - if (study != null) - { - Container container = getContainer(); - RepositorySettings settings = SettingsManager.get().getRepositorySettings(container); - ArrayList groupings = new ArrayList<>(2); - groupings.add(form.getGrouping1()); - groupings.add(form.getGrouping2()); - settings.setSpecimenWebPartGroupings(groupings); - SettingsManager.get().saveRepositorySettings(container, settings); - response.put("success", true); - return response; - } - else - throw new IllegalStateException("A study does not exist in this folder"); - } - } - - @RequiresSiteAdmin - public static class PivotAction extends SimpleViewAction - { - @Override - public ModelAndView getView(Object o, BindException errors) - { - return new JspView<>("/org/labkey/specimen/view/pivot.jsp"); - } - - @Override - public void addNavTrail(NavTree root) - { - } - } - - public static class PipelineForm extends PipelinePathForm - { - private String replaceOrMerge = "replace"; - - public String getReplaceOrMerge() - { - return replaceOrMerge; - } - - public void setReplaceOrMerge(String replaceOrMerge) - { - this.replaceOrMerge = replaceOrMerge; - } - - public boolean isMerge() - { - return "merge".equals(this.replaceOrMerge); - } - } - - public static void submitSpecimenBatch(Container c, User user, ActionURL url, File f, PipeRoot root, boolean merge) throws IOException - { - if (null == f || !f.exists() || !f.isFile()) - throw new NotFoundException(); - - SpecimenBatch batch = new SpecimenBatch(new ViewBackgroundInfo(c, user, url), f, root, merge); - batch.submit(); - } - - @RequiresPermission(AdminPermission.class) - public class SubmitSpecimenBatchImportAction extends FormHandlerAction - { - @Override - public void validateCommand(PipelineForm target, Errors errors) - { - } - - @Override - public boolean handlePost(PipelineForm form, BindException errors) throws Exception - { - Container c = getContainer(); - PipeRoot root = PipelineService.get().findPipelineRoot(c); - boolean first = true; - for (File f : form.getValidatedFiles(c)) - { - // Only possibly overwrite when the first archive is loaded: - boolean merge = !first || form.isMerge(); - submitSpecimenBatch(c, getUser(), getViewContext().getActionURL(), f, root, merge); - first = false; - } - return true; - } - - @Override - public ActionURL getSuccessURL(PipelineForm pipelineForm) - { - return urlProvider(PipelineStatusUrls.class).urlBegin(getContainer()); - } - } - - /** - * Legacy method hit via WGET/CURL to programmatically initiate a specimen import; no longer used by the UI, - * but this method should be kept around until we receive verification that the URL is no longer being hit - * programmatically. - */ - @RequiresPermission(AdminPermission.class) - public class SubmitSpecimenImport extends FormHandlerAction - { - @Override - public void validateCommand(PipelineForm target, Errors errors) - { - } - - @Override - public boolean handlePost(PipelineForm form, BindException errors) throws Exception - { - Container c = getContainer(); - String path = form.getPath(); - File f = null; - - PipeRoot root = PipelineService.get().findPipelineRoot(c); - if (path != null) - { - if (root != null) - f = root.resolvePath(path); - } - - submitSpecimenBatch(c, getUser(), getViewContext().getActionURL(), f, root, form.isMerge()); - return true; - } - - @Override - public ActionURL getSuccessURL(PipelineForm pipelineForm) - { - return urlProvider(PipelineStatusUrls.class).urlBegin(getContainer()); - } - } - - - public static class ImportSpecimensBean - { - private final String _path; - private final List _archives; - private final List _errors; - private final Container _container; - private final String[] _files; - - private boolean noSpecimens = false; - private boolean _defaultMerge = false; - private boolean _isEditableSpecimens = false; - - public ImportSpecimensBean(Container container, List archives, - String path, String[] files, List errors) - { - _path = path; - _files = files; - _archives = archives; - _errors = errors; - _container = container; - } - - public List getArchives() - { - return _archives; - } - - public String getPath() - { - return _path; - } - - public String[] getFiles() - { - return _files; - } - - public List getErrors() - { - return _errors; - } - - public Container getContainer() - { - return _container; - } - - public boolean isNoSpecimens() - { - return noSpecimens; - } - - public void setNoSpecimens(boolean noSpecimens) - { - this.noSpecimens = noSpecimens; - } - - public boolean isDefaultMerge() - { - return _defaultMerge; - } - - public void setDefaultMerge(boolean defaultMerge) - { - _defaultMerge = defaultMerge; - } - - public boolean isEditableSpecimens() - { - return _isEditableSpecimens; - } - - public void setEditableSpecimens(boolean editableSpecimens) - { - _isEditableSpecimens = editableSpecimens; - } - } - - @RequiresPermission(AdminPermission.class) - public class ImportSpecimenDataAction extends SimpleViewAction - { - private String[] _filePaths = null; - - @Override - public ModelAndView getView(PipelineForm form, BindException bindErrors) - { - List dataFiles = form.getValidatedFiles(getContainer()); - List archives = new ArrayList<>(); - List errors = new ArrayList<>(); - _filePaths = form.getFile(); - for (File dataFile : dataFiles) - { - if (null == dataFile || !dataFile.exists() || !dataFile.isFile()) - { - throw new NotFoundException(); - } - - if (!dataFile.canRead()) - errors.add("Can't read data file: " + dataFile); - - SpecimenArchive archive = new SpecimenArchive(dataFile); - archives.add(archive); - } - - ImportSpecimensBean bean = new ImportSpecimensBean(getContainer(), archives, form.getPath(), form.getFile(), errors); - boolean isEmpty = SpecimenManagerNew.get().isSpecimensEmpty(getContainer(), getUser()); - if (isEmpty) - { - bean.setNoSpecimens(true); - } - else if (SettingsManager.get().getRepositorySettings(getStudyThrowIfNull().getContainer()).isSpecimenDataEditable()) - { - bean.setDefaultMerge(true); // Repository is editable; make Merge the default - bean.setEditableSpecimens(true); - } - - return new JspView<>("/org/labkey/specimen/view/importSpecimens.jsp", bean); - } - - @Override - public void addNavTrail(NavTree root) - { - String msg; - if (_filePaths.length == 1) - msg = _filePaths[0]; - else - msg = _filePaths.length + " specimen archives"; - root.addChild("Import Study Batch - " + msg); - } - } - - @RequiresPermission(AdminPermission.class) - public class GetSpecimenExcelAction extends SimpleViewAction - { - @Override - public ModelAndView getView(Object o, BindException errors) - { - List> defaultSpecimens = new ArrayList<>(); - SimpleSpecimenImporter importer = new SimpleSpecimenImporter(getContainer(), getUser(), - getStudyRedirectIfNull().getTimepointType(), StudyService.get().getSubjectNounSingular(getContainer())); - MapArrayExcelWriter xlWriter = new MapArrayExcelWriter(defaultSpecimens, importer.getSimpleSpecimenColumns()); - for (ExcelColumn col : xlWriter.getColumns()) - col.setCaption(importer.label(col.getName())); - - xlWriter.write(getViewContext().getResponse()); - - return null; - } - - @Override - public void addNavTrail(NavTree root) - { - } - } - - static class SpecimenEventForm - { - private String _id; - private Container _targetStudy; - - public String getId() - { - return _id; - } - - public void setId(String id) - { - _id = id; - } - - public Container getTargetStudy() - { - return _targetStudy; - } - - public void setTargetStudy(Container targetStudy) - { - _targetStudy = targetStudy; - } - } - - @SuppressWarnings("unused") // Referenced in SpecimenForeignKey - @RequiresPermission(ReadPermission.class) - public static class SpecimenEventsRedirectAction extends SimpleViewAction - { - @Override - public ModelAndView getView(SpecimenEventForm form, BindException errors) - { - if (form.getId() != null && form.getTargetStudy() != null) - { - Vial vial = SpecimenManagerNew.get().getVial(form.getTargetStudy(), getUser(), form.getId()); - if (vial != null) - { - ActionURL url = new ActionURL(SpecimenEventsAction.class, form.getTargetStudy()).addParameter("id", vial.getRowId()); - throw new RedirectException(url); - } - } - return new HtmlView("Unable to resolve the Specimen ID and target Study"); - } - - @Override - public void addNavTrail(NavTree root) - { - } - } - - public static class SpecimenEventAttachmentForm - { - private int _eventId; - private String _name; - - public int getEventId() - { - return _eventId; - } - - public void setEventId(int eventId) - { - _eventId = eventId; - } - - public String getName() - { - return _name; - } - - public void setName(String name) - { - _name = name; - } - } - - public static ActionURL getDownloadURL(SpecimenRequestEvent event, String name) - { - return new ActionURL(DownloadAction.class, event.getContainer()) - .addParameter("eventId", event.getRowId()) - .addParameter("name", name); - } - - @RequiresPermission(ReadPermission.class) - public static class DownloadAction extends BaseDownloadAction - { - @Override - public @Nullable Pair getAttachment(SpecimenEventAttachmentForm form) - { - SpecimenRequestEvent event = SpecimenRequestManager.get().getRequestEvent(getContainer(), form.getEventId()); - if (event == null) - throw new NotFoundException("Specimen event not found"); - - return new Pair<>(event, form.getName()); - } - } - - @RequiresPermission(ReadPermission.class) - public class AutoReportListAction extends SimpleViewAction - { - @Override - public ModelAndView getView(Object form, BindException errors) - { - return new JspView<>("/org/labkey/specimen/view/autoReportList.jsp", new ReportConfigurationBean(getViewContext())); - } - - @Override - public void addNavTrail(NavTree root) - { - setHelpTopic("exploreSpecimens"); - addBaseSpecimenNavTrail(root); - root.addChild("Specimen Reports"); - } - } - - @RequiresPermission(ReadPermission.class) - public class SpecimenRequestConfigRequiredAction extends SimpleViewAction - { - @Override - public ModelAndView getView(Object o, BindException errors) - { - return new JspView<>("/org/labkey/specimen/view/configurationRequired.jsp"); - } - - @Override - public void addNavTrail(NavTree root) - { - addRootNavTrail(root); - root.addChild("Unable to Request Specimens"); - } - } - - @RequiresPermission(ManageRequestSettingsPermission.class) - public class ConfigureRequestabilityRulesAction extends SimpleViewAction - { - @Override - public ModelAndView getView(Object o, BindException errors) - { - ensureSpecimenRequestsConfigured(false); - return new JspView<>("/org/labkey/specimen/view/configRequestabilityRules.jsp"); - } - - @Override - public void addNavTrail(NavTree root) - { - setHelpTopic("coordinateSpecimens#requestability"); - addManageStudyNavTrail(root); - root.addChild("Configure Requestability Rules"); - } - } - - @RequiresPermission(ReadPermission.class) - public class ViewRequestsAction extends SimpleViewAction - { - @Override - public ModelAndView getView(Object o, BindException errors) - { - requiresLogin(); - SpecimenRequestQueryView grid = SpecimenRequestQueryView.createView(getViewContext()); - grid.setExtraLinks(true); - grid.setShowCustomizeLink(false); - grid.setShowDetailsColumn(false); - if (getContainer().hasPermission(getUser(), RequestSpecimensPermission.class)) - { - ActionButton insertButton = new ActionButton(new ActionURL(ShowCreateSpecimenRequestAction.class, getContainer()), "Create New Request", ActionButton.Action.LINK); - grid.setButtons(Collections.singletonList(insertButton)); - } - else - { - grid.setButtons(Collections.emptyList()); - } - - JspView header = new JspView<>("/org/labkey/specimen/view/viewRequestsHeader.jsp", - new ViewRequestsHeaderBean(getViewContext(), grid)); - - return new VBox(header, grid); - } - - @Override - public void addNavTrail(NavTree root) - { - addSpecimenRequestsNavTrail(root); - } - } - - public static class ViewEventForm extends IdForm - { - private boolean _selected; - private boolean _vialView; - - public boolean isSelected() - { - return _selected; - } - - public void setSelected(boolean selected) - { - _selected = selected; - } - - public boolean isVialView() - { - return _vialView; - } - - public void setVialView(boolean vialView) - { - _vialView = vialView; - } - } - - @RequiresPermission(ReadPermission.class) - public class SpecimenEventsAction extends SimpleViewAction - { - private boolean _showingSelectedSpecimens; - - @Override - public ModelAndView getView(ViewEventForm viewEventForm, BindException errors) - { - Study study = getStudyThrowIfNull(); - _showingSelectedSpecimens = viewEventForm.isSelected(); - Vial vial = SpecimenManagerNew.get().getVial(getContainer(), getUser(), viewEventForm.getId()); - if (vial == null) - throw new NotFoundException("Specimen " + viewEventForm.getId() + " does not exist."); - - JspView summaryView = new JspView<>("/org/labkey/study/view/specimen/specimen.jsp", - new SpecimenEventBean(vial, viewEventForm.getReturnUrl())); - summaryView.setTitle("Vial Summary"); - - SpecimenEventQueryView vialHistoryView = SpecimenEventQueryView.createView(getViewContext(), vial); - vialHistoryView.setTitle("Vial History"); - - VBox vbox; - - if (SettingsManager.get().getRepositorySettings(getStudyRedirectIfNull().getContainer()).isEnableRequests()) - { - List requestIds = SpecimenRequestManager.get().getRequestIdsForSpecimen(vial); - SimpleFilter requestFilter; - WebPartView relevantRequests; - - if (!requestIds.isEmpty()) - { - requestFilter = new SimpleFilter(); - StringBuilder whereClause = new StringBuilder(); - for (int i = 0; i < requestIds.size(); i++) - { - if (i > 0) - whereClause.append(" OR "); - whereClause.append("RequestId = ?"); - } - requestFilter.addWhereClause(whereClause.toString(), requestIds.toArray()); - SpecimenRequestQueryView queryView = SpecimenRequestQueryView.createView(getViewContext(), requestFilter); - queryView.setExtraLinks(true); - relevantRequests = queryView; - } - else - relevantRequests = new JspView("/org/labkey/specimen/view/relevantRequests.jsp"); - relevantRequests.setTitle("Relevant Vial Requests"); - vbox = new VBox(summaryView, vialHistoryView, relevantRequests); - } - else - { - vbox = new VBox(summaryView, vialHistoryView); - } - - return vbox; - } - - @Override - public void addNavTrail(NavTree root) - { - addBaseSpecimenNavTrail(root); - if (_showingSelectedSpecimens) - { - root.addChild("Selected Specimens", urlProvider(SpecimenUrls.class).getSpecimensURL(getContainer(), true)); - } - root.addChild("Vial History"); - } - } - - private boolean isNullOrBlank(String toCheck) - { - return ((toCheck == null) || toCheck.equals("")); - } - - @RequiresPermission(ManageNotificationsPermission.class) - public class ManageNotificationsAction extends FormViewAction - { - @Override - public void validateCommand(RequestNotificationSettings form, Errors errors) - { - String replyTo = form.getReplyTo(); - if (replyTo == null || replyTo.length() == 0) - { - errors.reject(SpringActionController.ERROR_MSG, "Reply-to cannot be empty."); - } - else if (!RequestNotificationSettings.REPLY_TO_CURRENT_USER_VALUE.equals(replyTo)) - { - try - { - new ValidEmail(replyTo); - } - catch(ValidEmail.InvalidEmailException e) - { - errors.reject(SpringActionController.ERROR_MSG, replyTo + " is not a valid email address."); - } - } - - String subjectSuffix = form.getSubjectSuffix(); - if (subjectSuffix == null || subjectSuffix.length() == 0) - { - errors.reject(SpringActionController.ERROR_MSG, "Subject suffix cannot be empty."); - } - - try - { - form.getNewRequestNotifyAddresses(); - } - catch (ValidEmail.InvalidEmailException e) - { - errors.reject(SpringActionController.ERROR_MSG, e.getBadEmail() + " is not a valid email address."); - } - - try - { - form.getCCAddresses(); - } - catch (ValidEmail.InvalidEmailException e) - { - errors.reject(SpringActionController.ERROR_MSG, e.getBadEmail() + " is not a valid email address."); - } - } - - @Override - public ModelAndView getView(RequestNotificationSettings form, boolean reshow, BindException errors) - { - ensureSpecimenRequestsConfigured(false); - - // try to get the settings from the form, just in case this is a reshow: - RequestNotificationSettings settings = form; - if (settings == null || settings.getReplyTo() == null) - settings = SettingsManager.get().getRequestNotificationSettings(getContainer()); - - return new JspView<>("/org/labkey/specimen/view/manageNotifications.jsp", settings, errors); - } - - @Override - public boolean handlePost(RequestNotificationSettings settings, BindException errors) - { - ensureSpecimenRequestsConfigured(false); - - if (!settings.isNewRequestNotifyCheckbox()) - settings.setNewRequestNotify(null); - else - { - if (isNullOrBlank(settings.getNewRequestNotify())) - errors.reject(ERROR_MSG, "New request notify is blank and send email is checked"); - } - if (!settings.isCcCheckbox()) - settings.setCc(null); - else - { - if (isNullOrBlank(settings.getCc())) - errors.reject(ERROR_MSG, "Always CC is blank and send email is checked"); - } - if (errors.hasErrors()) - return false; - - SettingsManager.get().saveRequestNotificationSettings(getContainer(), settings); - return true; - } - - @Override - public ActionURL getSuccessURL(RequestNotificationSettings form) - { - return getManageStudyURL(); - } - - @Override - public void addNavTrail(NavTree root) - { - setHelpTopic("coordinateSpecimens#notify"); - addManageStudyNavTrail(root); - root.addChild("Manage Notifications"); - } - } - - @RequiresPermission(ManageDisplaySettingsPermission.class) - public class ManageDisplaySettingsAction extends FormViewAction - { - @Override - public void validateCommand(DisplaySettingsForm target, Errors errors) - { - } - - @Override - public ModelAndView getView(DisplaySettingsForm form, boolean reshow, BindException errors) - { - // try to get the settings from the form, just in case this is a reshow: - DisplaySettings settings = form.getBean(); - if (settings == null || settings.getLastVialEnum() == null) - settings = SettingsManager.get().getDisplaySettings(getContainer()); - - return new JspView<>("/org/labkey/specimen/view/manageDisplay.jsp", settings); - } - - @Override - public boolean handlePost(DisplaySettingsForm form, BindException errors) - { - DisplaySettings settings = form.getBean(); - SettingsManager.get().saveDisplaySettings(getContainer(), settings); - - return true; - } - - @Override - public ActionURL getSuccessURL(DisplaySettingsForm displaySettingsForm) - { - return getManageStudyURL(); - } - - @Override - public void addNavTrail(NavTree root) - { - setHelpTopic(new HelpTopic("specimenRequest#display")); - addManageStudyNavTrail(root); - root.addChild("Manage Specimen Display Settings"); - } - } - - public static class DisplaySettingsForm extends BeanViewForm - { - public DisplaySettingsForm() - { - super(DisplaySettings.class); - } - } - - public static class UpdateRequestabilityRulesForm implements HasViewContext - { - private ViewContext _viewContext; - private String[] _ruleType; - private String[] _ruleData; - private String[] _markType; - - @Override - public ViewContext getViewContext() - { - return _viewContext; - } - - @Override - public void setViewContext(ViewContext viewContext) - { - _viewContext = viewContext; - } - - public String[] getRuleType() - { - return _ruleType; - } - - @SuppressWarnings("unused") - public void setRuleType(String[] ruleType) - { - _ruleType = ruleType; - } - - public String[] getRuleData() - { - return _ruleData; - } - - @SuppressWarnings("unused") - public void setRuleData(String[] ruleData) - { - _ruleData = ruleData; - } - - public String[] getMarkType() - { - return _markType; - } - - public void setMarkType(String[] markType) - { - _markType = markType; - } - } - - @RequiresPermission(ManageRequestSettingsPermission.class) - public static class UpdateRequestabilityRulesAction extends MutatingApiAction - { - @Override - public ApiResponse execute(UpdateRequestabilityRulesForm form, BindException errors) - { - final List rules = new ArrayList<>(); - for (int i = 0; i < form.getRuleType().length; i++) - { - String typeName = form.getRuleType()[i]; - RequestabilityManager.RuleType type = RequestabilityManager.RuleType.valueOf(typeName); - String dataString = form.getRuleData()[i]; - rules.add(type.createRule(getContainer(), dataString)); - } - RequestabilityManager.getInstance().saveRules(getContainer(), getUser(), rules); - - return new ApiSimpleResponse(Collections.singletonMap("savedCount", rules.size())); - } - } - - @RequiresPermission(ManageStudyPermission.class) - public class ManageRepositorySettingsAction extends FormViewAction - { - @Override - public ModelAndView getView(ManageRepositorySettingsForm from, boolean reshow, BindException errors) - { - return new JspView<>("/org/labkey/specimen/view/manageRepositorySettings.jsp", SettingsManager.get().getRepositorySettings(getContainer())); - } - - @Override - public void validateCommand(ManageRepositorySettingsForm form, Errors errors) - { - } - - @Override - public boolean handlePost(ManageRepositorySettingsForm form, BindException errors) throws Exception - { - RepositorySettings settings = SettingsManager.get().getRepositorySettings(getContainer()); - settings.setSimple(form.isSimple()); - settings.setEnableRequests(!form.isSimple() && form.isEnableRequests()); - settings.setSpecimenDataEditable(!form.isSimple() && form.isSpecimenDataEditable()); - SettingsManager.get().saveRepositorySettings(getContainer(), settings); - - return true; - } - - @Override - public URLHelper getSuccessURL(ManageRepositorySettingsForm manageRepositorySettingsForm) - { - return getManageStudyURL(); - } - - @Override - public void addNavTrail(NavTree root) - { - setHelpTopic("specimenAdminTutorial"); - addManageStudyNavTrail(root); - root.addChild("Manage Repository Settings"); - } - } - - public static class ManageRepositorySettingsForm - { - private boolean _simple; - private boolean _enableRequests; - private boolean _specimenDataEditable; - - public boolean isSimple() - { - return _simple; - } - - public void setSimple(boolean simple) - { - _simple = simple; - } - - public boolean isEnableRequests() - { - return _enableRequests; - } - - public void setEnableRequests(boolean enableRequests) - { - _enableRequests = enableRequests; - } - - public boolean isSpecimenDataEditable() - { - return _specimenDataEditable; - } - - public void setSpecimenDataEditable(boolean specimenDataEditable) - { - _specimenDataEditable = specimenDataEditable; - } - } - - public static class DefaultRequirementsForm - { - private int _originatorActor; - private String _originatorDescription; - private int _providerActor; - private String _providerDescription; - private int _receiverActor; - private String _receiverDescription; - private int _generalActor; - private String _generalDescription; - private String _nextPage; - - public int getGeneralActor() - { - return _generalActor; - } - - @SuppressWarnings("unused") - public void setGeneralActor(int generalActor) - { - _generalActor = generalActor; - } - - public String getGeneralDescription() - { - return _generalDescription; - } - - @SuppressWarnings("unused") - public void setGeneralDescription(String generalDescription) - { - _generalDescription = generalDescription; - } - - public int getProviderActor() - { - return _providerActor; - } - - @SuppressWarnings("unused") - public void setProviderActor(int providerActor) - { - _providerActor = providerActor; - } - - public String getProviderDescription() - { - return _providerDescription; - } - - @SuppressWarnings("unused") - public void setProviderDescription(String providerDescription) - { - _providerDescription = providerDescription; - } - - public int getReceiverActor() - { - return _receiverActor; - } - - @SuppressWarnings("unused") - public void setReceiverActor(int receiverActor) - { - _receiverActor = receiverActor; - } - - public String getReceiverDescription() - { - return _receiverDescription; - } - - @SuppressWarnings("unused") - public void setReceiverDescription(String receiverDescription) - { - _receiverDescription = receiverDescription; - } - - public int getOriginatorActor() - { - return _originatorActor; - } - - @SuppressWarnings("unused") - public void setOriginatorActor(int originatorActor) - { - _originatorActor = originatorActor; - } - - public String getOriginatorDescription() - { - return _originatorDescription; - } - - @SuppressWarnings("unused") - public void setOriginatorDescription(String originatorDescription) - { - _originatorDescription = originatorDescription; - } - - public String getNextPage() - { - return _nextPage; - } - - @SuppressWarnings("unused") - public void setNextPage(String nextPage) - { - _nextPage = nextPage; - } - } - - @RequiresPermission(ManageRequestRequirementsPermission.class) - public class ManageDefaultReqsAction extends FormViewAction - { - @Override - public void validateCommand(DefaultRequirementsForm target, Errors errors) - { - } - - @Override - public ModelAndView getView(DefaultRequirementsForm defaultRequirementsForm, boolean reshow, BindException errors) - { - ensureSpecimenRequestsConfigured(true); - return new JspView<>("/org/labkey/specimen/view/manageDefaultReqs.jsp", - new ManageReqsBean(getUser(), getContainer())); - } - - @Override - public boolean handlePost(DefaultRequirementsForm form, BindException errors) - { - ensureSpecimenRequestsConfigured(true); - createDefaultRequirement(form.getOriginatorActor(), form.getOriginatorDescription(), SpecimenRequestRequirementType.ORIGINATING_SITE); - createDefaultRequirement(form.getProviderActor(), form.getProviderDescription(), SpecimenRequestRequirementType.PROVIDING_SITE); - createDefaultRequirement(form.getReceiverActor(), form.getReceiverDescription(), SpecimenRequestRequirementType.RECEIVING_SITE); - createDefaultRequirement(form.getGeneralActor(), form.getGeneralDescription(), SpecimenRequestRequirementType.NON_SITE_BASED); - return true; - } - - private void createDefaultRequirement(Integer actorId, String description, SpecimenRequestRequirementType type) - { - if (actorId != null && actorId.intValue() > 0 && description != null && description.length() > 0) - { - SpecimenRequestRequirement requirement = new SpecimenRequestRequirement(); - requirement.setContainer(getContainer()); - requirement.setActorId(actorId); - requirement.setDescription(description); - requirement.setRequestId(-1); - SpecimenRequestRequirementProvider.get().createDefaultRequirement(getUser(), requirement, type); - } - } - - @Override - public ActionURL getSuccessURL(DefaultRequirementsForm form) - { - if (form.getNextPage() != null && form.getNextPage().length() > 0) - return new ActionURL(form.getNextPage()); - else - return getManageStudyURL(); - } - - @Override - public void addNavTrail(NavTree root) - { - setHelpTopic("coordinateSpecimens#requirements"); - addManageStudyNavTrail(root); - root.addChild("Manage Default Requirements"); - } - } - - @RequiresPermission(ManageRequestRequirementsPermission.class) - public class DeleteDefaultRequirementAction extends FormHandlerAction - { - @Override - public void validateCommand(IdForm target, Errors errors) - { - } - - @Override - public boolean handlePost(IdForm form, BindException errors) throws Exception - { - SpecimenRequestRequirement requirement = - SpecimenRequestRequirementProvider.get().getRequirement(getContainer(), form.getId()); - // we should only be deleting default requirements (those without an associated request): - if (requirement != null && requirement.getRequestId() == -1) - { - SpecimenRequestManager.get().deleteRequestRequirement(getUser(), requirement, false); - return true; - } - - return false; - } - - @Override - public ActionURL getSuccessURL(IdForm requirementForm) - { - return new ActionURL(ManageDefaultReqsAction.class, getContainer()); - } - } - - public static class CompleteSpecimenForm - { - private String _prefix; - - public String getPrefix() - { - return _prefix; - } - - public void setPrefix(String prefix) - { - _prefix = prefix; - } - } - - @RequiresPermission(AdminPermission.class) - public class CompleteSpecimenAction extends ReadOnlyApiAction - { - @Override - public ApiResponse execute(CompleteSpecimenForm form, BindException errors) - { - ApiSimpleResponse response = new ApiSimpleResponse(); - - Study study = getStudy(); - if (study == null) - throw new NotFoundException("No study exists in this folder."); - - List completions = new ArrayList<>(); - for (AjaxCompletion completion : getAjaxCompletions(study)) - completions.add(completion.toJSON()); - - response.put("completions", completions); - return response; - } - } - - private List getAjaxCompletions(Study study) - { - List completions = new ArrayList<>(); - String allString = "All " + PageFlowUtil.filter(StudyService.get().getSubjectNounPlural(study.getContainer())) + " (Large Report)"; - - completions.add(new AjaxCompletion(allString, allString)); - - for (String ptid : StudyService.get().getParticipantIds(study, getViewContext().getUser())) - completions.add(new AjaxCompletion(ptid, ptid)); - - return completions; - } - public static List getSpecimensFromRowIds(long[] requestedSampleIds, Container container, User user) - { - List requestedVials = null; - - if (requestedSampleIds != null) - { - List vials = new ArrayList<>(); - for (long requestedSampleId : requestedSampleIds) - { - Vial current = SpecimenManagerNew.get().getVial(container, user, requestedSampleId); - if (current != null) - vials.add(current); - } - requestedVials = vials; - } - - return requestedVials; - } - - private static long[] toLongArray(Collection intStrings) - { - if (intStrings == null) - return null; - long[] converted = new long[intStrings.size()]; - int index = 0; - for (String intString : intStrings) - converted[index++] = Long.parseLong(intString); - return converted; - } - - public static List getSpecimensFromRowIds(Collection ids, Container container, User user) - { - return getSpecimensFromRowIds(toLongArray(ids), container, user); - } - - public List getSpecimensFromPost(boolean fromGroupedView, boolean onlyAvailable) - { - Set formValues = null; - if ("POST".equalsIgnoreCase(getViewContext().getRequest().getMethod())) - formValues = DataRegionSelection.getSelected(getViewContext(), true); - - if (formValues == null || formValues.isEmpty()) - return null; - - List selectedVials; - if (fromGroupedView) - { - Map> keyToVialMap = - SpecimenManagerNew.get().getVialsForSpecimenHashes(getContainer(), getUser(), formValues, onlyAvailable); - List vials = new ArrayList<>(); - for (List vialList : keyToVialMap.values()) - vials.addAll(vialList); - selectedVials = new ArrayList<>(vials); - } - else - selectedVials = getSpecimensFromRowIds(formValues, getContainer(), getUser()); - return selectedVials; - } - - public static class CreateSpecimenRequestForm extends ReturnUrlForm implements HiddenFormInputGenerator - { - public enum PARAMS - { - returnUrl, - extendedRequestUrl, - ignoreReturnUrl - } - - private String[] _inputs; - private int _destinationLocation; - private long[] _specimenRowIds; - private boolean[] _required; - private boolean _fromGroupedView; - private Integer _preferredLocation; - private boolean _ignoreReturnUrl; - private boolean _extendedRequestUrl; - private String[] _specimenIds; - - @Override - public String getHiddenFormInputs(ViewContext ctx) - { - StringBuilder builder = new StringBuilder(); - if (_inputs != null) - { - for (String input : _inputs) - builder.append("\n"); - } - if (_destinationLocation != 0) - builder.append("\n"); - if (getReturnUrl() != null) - builder.append("\n"); - if (_specimenRowIds != null) - { - for (long specimenId : _specimenRowIds) - builder.append("\n"); - } - else - { - String dataRegionSelectionKey = ctx.getRequest().getParameter(DataRegionSelection.DATA_REGION_SELECTION_KEY); - if (dataRegionSelectionKey != null) - { - builder.append("\n"); - Set specimenFormValues = DataRegionSelection.getSelected(ctx, false); - for (String formValue : specimenFormValues) - { - builder.append("\n"); - } - } - } - - if (_required != null) - { - for (boolean required : _required) - builder.append("\n"); - } - - if (_specimenIds != null) - { - for (String specimenId : _specimenIds) - builder.append("\n"); - } - - builder.append("\n"); - return builder.toString(); - } - - public long[] getSpecimenRowIds() - { - return _specimenRowIds; - } - - public void setSpecimenRowIds(long[] specimenRowIds) - { - _specimenRowIds = specimenRowIds; - } - - public int getDestinationLocation() - { - return _destinationLocation; - } - - public void setDestinationLocation(int destinationLocation) - { - _destinationLocation = destinationLocation; - } - - public String[] getInputs() - { - return _inputs; - } - - public void setInputs(String[] inputs) - { - _inputs = inputs; - } - - public boolean[] getRequired() - { - return _required; - } - - public void setRequired(boolean[] required) - { - _required = required; - } - - public boolean isFromGroupedView() - { - return _fromGroupedView; - } - - public void setFromGroupedView(boolean fromGroupedView) - { - _fromGroupedView = fromGroupedView; - } - - public Integer getPreferredLocation() - { - return _preferredLocation; - } - - public void setPreferredLocation(Integer preferredLocation) - { - _preferredLocation = preferredLocation; - } - - public boolean isIgnoreReturnUrl() - { - return _ignoreReturnUrl; - } - - public void setIgnoreReturnUrl(boolean ignoreReturnUrl) - { - _ignoreReturnUrl = ignoreReturnUrl; - } - - public boolean isExtendedRequestUrl() - { - return _extendedRequestUrl; - } - - public void setExtendedRequestUrl(boolean extendedRequestUrl) - { - _extendedRequestUrl = extendedRequestUrl; - } - - public String[] getSpecimenIds() - { - return _specimenIds; - } - - public void setSpecimenIds(String[] specimenIds) - { - _specimenIds = specimenIds; - } - - public RequestedSpecimens getSelectedSpecimens(ViewContext ctx) throws AmbiguousLocationException - { - Container container = ctx.getContainer(); - User user = ctx.getUser(); - HttpServletRequest request = ctx.getRequest(); - - // first check for explicitly listed specimen row ids (this is the case when posting the final - // specimen request form): - List requestedSpecimens = getSpecimensFromRowIds(getSpecimenRowIds(), container, user); - if (requestedSpecimens != null && requestedSpecimens.size() > 0) - return new RequestedSpecimens(requestedSpecimens); - - Set ids; - if ("post".equalsIgnoreCase(request.getMethod()) && - (request.getParameter(DataRegionSelection.DATA_REGION_SELECTION_KEY) != null)) - { - ids = DataRegionSelection.getSelected(ctx, null, true); - if (isFromGroupedView()) - return SpecimenRequestManager.get().getRequestableBySpecimenHash(container, user, ids, getPreferredLocation()); - else - return getRequestableByVialRowIds(ids, container, user); - } - else if (_specimenIds != null && _specimenIds.length > 0) - { - ids = new HashSet<>(); - Collections.addAll(ids, _specimenIds); - if (isFromGroupedView()) - return SpecimenRequestManager.get().getRequestableBySpecimenHash(container, user, ids, getPreferredLocation()); - else - return getRequestableByVialGlobalUniqueIds(ids, container, user); - } - else - return null; - } - - private RequestedSpecimens getRequestableByVialRowIds(Set rowIds, Container container, User user) - { - Set ids = new HashSet<>(); - Arrays.stream(toLongArray(rowIds)).forEach(ids::add); - List requestedSpecimens = SpecimenManagerNew.get().getRequestableVials(container, user, ids); - return new RequestedSpecimens(requestedSpecimens); - } - - private RequestedSpecimens getRequestableByVialGlobalUniqueIds(Set globalUniqueIds, Container container, User user) - { - List requestedVials = null; - - if (globalUniqueIds != null) - { - List vials = new ArrayList<>(); - for (String globalUniqueId : globalUniqueIds) - { - Vial match = SpecimenManagerNew.get().getVial(container, user, globalUniqueId); - if (match != null) - vials.add(match); - } - requestedVials = new ArrayList<>(vials); - } - return new RequestedSpecimens(requestedVials); - } - } - - public abstract static class SpecimensViewBean - { - protected SpecimenQueryView _specimenQueryView; - protected List _vials; - - public SpecimensViewBean(ViewContext context, List vials, boolean showHistoryLinks, - boolean showRecordSelectors, boolean disableLowVialIndicators, boolean restrictRecordSelectors) - { - _vials = vials; - if (vials != null && vials.size() > 0) - { - _specimenQueryView = SpecimenQueryView.createView(context, vials, SpecimenQueryView.ViewType.VIALS); - _specimenQueryView.setShowHistoryLinks(showHistoryLinks); - _specimenQueryView.setShowRecordSelectors(showRecordSelectors); - _specimenQueryView.setDisableLowVialIndicators(disableLowVialIndicators); - _specimenQueryView.setRestrictRecordSelectors(restrictRecordSelectors); - } - } - - public SpecimenQueryView getSpecimenQueryView() - { - return _specimenQueryView; - } - - public List getVials() - { - return _vials; - } - } - - public static class NewRequestBean extends SpecimensViewBean - { - private final Container _container; - private final SpecimenRequestManager.SpecimenRequestInput[] _inputs; - private final String[] _inputValues; - private final int _selectedSite; - private final BindException _errors; - private final ActionURL _returnUrl; - - public NewRequestBean(ViewContext context, RequestedSpecimens requestedSpecimens, CreateSpecimenRequestForm form, BindException errors) throws SQLException - { - super(context, requestedSpecimens != null ? requestedSpecimens.getVials() : null, false, false, false, false); - _errors = errors; - _inputs = SpecimenRequestManager.get().getNewSpecimenRequestInputs(context.getContainer()); - _selectedSite = form.getDestinationLocation(); - _inputValues = form.getInputs(); - _container = context.getContainer(); - _returnUrl = form.getReturnActionURL(); - } - - public SpecimenRequestManager.SpecimenRequestInput[] getInputs() - { - return _inputs; - } - - public String getValue(int inputIndex) throws ValidationException - { - if (_inputValues != null && inputIndex < _inputValues.length && _inputValues[inputIndex] != null) - return _inputValues[inputIndex]; - if (_inputs[inputIndex].isRememberSiteValue() && _selectedSite > 0) - return _inputs[inputIndex].getDefaultSiteValues(_container).get(_selectedSite); - return ""; - } - - public int getSelectedSite() - { - return _selectedSite; - } - - public BindException getErrors() - { - return _errors; - } - - public ActionURL getReturnUrl() - { - return _returnUrl; - } - } - - @RequiresPermission(RequestSpecimensPermission.class) - public class HandleCreateSpecimenRequestAction extends FormViewAction - { - private SpecimenRequest _specimenRequest; - - @Override - public ModelAndView getView(CreateSpecimenRequestForm form, boolean reshow, BindException errors) throws Exception - { - return getCreateSpecimenRequestView(form, errors); - } - - @Override - public void addNavTrail(NavTree root) - { - addSpecimenRequestsNavTrail(root); - root.addChild("New Specimen Request"); - } - - @Override - public void validateCommand(CreateSpecimenRequestForm form, Errors errors) - { - boolean missingRequiredInput = false; - if (form.getDestinationLocation() <= 0) - missingRequiredInput = true; - - for (int i = 0; i < form.getInputs().length && !missingRequiredInput; i++) - { - if (form.getRequired()[i] && (form.getInputs()[i] == null || form.getInputs()[i].length() == 0)) - missingRequiredInput = true; - } - - if (missingRequiredInput) - errors.reject(null, "Please provide all required input."); - } - - @Override - public boolean handlePost(CreateSpecimenRequestForm form, BindException errors) throws Exception - { - ensureSpecimenRequestsConfigured(true); - - String[] inputs = form.getInputs(); - long[] specimenIds = form.getSpecimenRowIds(); - StringBuilder comments = new StringBuilder(); - SpecimenRequestManager.SpecimenRequestInput[] expectedInputs = - SpecimenRequestManager.get().getNewSpecimenRequestInputs(getContainer()); - if (inputs.length != expectedInputs.length) - throw new IllegalStateException("Expected a form element for each input."); - - for (int i = 0; i < expectedInputs.length; i++) - { - SpecimenRequestManager.SpecimenRequestInput expectedInput = expectedInputs[i]; - if (form.getDestinationLocation() != 0 && expectedInput.isRememberSiteValue()) - expectedInput.setDefaultSiteValue(getContainer(), form.getDestinationLocation(), inputs[i]); - if (i > 0) - comments.append("\n\n"); - comments.append(expectedInput.getTitle()).append(":\n"); - if (inputs[i] != null && inputs[i].length() > 0) - comments.append(inputs[i]); - else - comments.append("[Not provided]"); - } - - _specimenRequest = new SpecimenRequest(); - _specimenRequest.setComments(comments.toString()); - _specimenRequest.setContainer(getContainer()); - Timestamp ts = new Timestamp(System.currentTimeMillis()); - _specimenRequest.setCreated(ts); - _specimenRequest.setModified(ts); - _specimenRequest.setEntityId(GUID.makeGUID()); - Integer defaultSiteId = SpecimenService.get().getRequestCustomizer().getDefaultDestinationSiteId(); - // Default takes precedence if set - if (defaultSiteId != null) - { - _specimenRequest.setDestinationSiteId(defaultSiteId); - } - else if (form.getDestinationLocation() > 0) - { - _specimenRequest.setDestinationSiteId(form.getDestinationLocation()); - } - _specimenRequest.setStatusId(SpecimenRequestManager.get().getInitialRequestStatus(getContainer(), getUser(), false).getRowId()); - - DbScope scope = SpecimenSchema.get().getScope(); - try (DbScope.Transaction transaction = scope.ensureTransaction()) - { - if (!LocationManager.get().isSiteValidRequestingLocation(getContainer(), _specimenRequest.getDestinationSiteId())) - { - errors.reject(ERROR_MSG, "The requesting location is not valid."); - return false; - } - - User user = getUser(); - Container container = getContainer(); - _specimenRequest = SpecimenRequestManager.get().createRequest(getUser(), _specimenRequest, true); - List vials; - if (specimenIds != null && specimenIds.length > 0) - { - vials = new ArrayList<>(); - for (long specimenId : specimenIds) - { - Vial vial = SpecimenManagerNew.get().getVial(container, user, specimenId); - if (vial != null) - { - boolean isAvailable = vial.isAvailable(); - if (!isAvailable) - { - errors.reject(null, RequestabilityManager.makeSpecimenUnavailableMessage(vial, "This specimen has been removed from the list below.")); - } - else - vials.add(vial); - } - else - { - ExceptionUtil.logExceptionToMothership(getViewContext().getRequest(), - new IllegalStateException("Specimen ID " + specimenId + " was not found in container " + container.getId())); - } - } - if (errors.getErrorCount() == 0) - { - try - { - SpecimenRequestManager.get().createRequestSpecimenMapping(getUser(), _specimenRequest, vials, true, true); - } - catch (RequestabilityManager.InvalidRuleException e) - { - errors.reject(ERROR_MSG, "The request could not be created because a requestability rule is configured incorrectly. " + - "Please report this problem to an administrator. Error details: " + e.getMessage()); - return false; - } - catch (SpecimenRequestException e) - { - errors.reject(ERROR_MSG, "A vial that was available for request has become unavailable."); - return false; - } - } - else - { - long[] validSpecimenIds = new long[vials.size()]; - int index = 0; - for (Vial vial : vials) - validSpecimenIds[index++] = vial.getRowId(); - form.setSpecimenRowIds(validSpecimenIds); - return false; - } - } - transaction.commit(); - } - - if (!SettingsManager.get().isSpecimenShoppingCartEnabled(getContainer())) - { - try - { - StudyInternalService.get().sendNewRequestNotifications(SpecimenController2.this, _specimenRequest, errors); - } - catch (ConfigurationException | IOException e) - { - errors.reject(ERROR_MSG, e.getMessage()); - } - } - - if (errors.hasErrors()) - return false; - - Study study = getStudy(); - if (null == study) - throw new NotFoundException("No study exists in this folder."); - StudyInternalService.get().setLastSpecimenRequest(study, _specimenRequest.getRowId()); - return true; - } - - @Override - public ActionURL getSuccessURL(CreateSpecimenRequestForm createSpecimenRequestForm) - { - ActionURL modifiedReturnURL = null; - if (createSpecimenRequestForm.isExtendedRequestUrl()) - { - return getExtendedRequestURL(_specimenRequest.getRowId(), null); - } - if (createSpecimenRequestForm.getReturnUrl() != null) - { - modifiedReturnURL = createSpecimenRequestForm.getReturnActionURL(); - } - if (modifiedReturnURL != null && !createSpecimenRequestForm.isIgnoreReturnUrl()) - return modifiedReturnURL; - else - return getManageRequestURL(_specimenRequest.getRowId(), modifiedReturnURL != null ? modifiedReturnURL : null); - } - } - - private ModelAndView getCreateSpecimenRequestView(CreateSpecimenRequestForm form, BindException errors) throws SQLException - { - ensureSpecimenRequestsConfigured(true); - - RequestedSpecimens requested; - - try - { - requested = form.getSelectedSpecimens(getViewContext()); - } - catch (AmbiguousLocationException e) - { - // Even though this method (getCreateSpecimenRequestView) is used from multiple places, only HandleCreateSpecimenRequestAction - // receives a post; therefore, it's safe to say that the selectSpecimenProvider.jsp form should always post to - // HandleCreateSpecimenRequestAction. - return new JspView<>("/org/labkey/specimen/view/selectSpecimenProvider.jsp", - new SelectSpecimenProviderBean(form, e.getPossibleLocations(), new ActionURL(ShowCreateSpecimenRequestAction.class, getContainer())), errors); - } - - return new JspView<>("/org/labkey/specimen/view/requestSpecimens.jsp", - new NewRequestBean(getViewContext(), requested, form, errors)); - } - - @RequiresPermission(RequestSpecimensPermission.class) - public class ShowCreateSpecimenRequestAction extends SimpleViewAction - { - @Override - public ModelAndView getView(CreateSpecimenRequestForm form, BindException errors) throws Exception - { - return getCreateSpecimenRequestView(form, errors); - } - - @Override - public void addNavTrail(NavTree root) - { - setHelpTopic("specimenShopping"); - addSpecimenRequestsNavTrail(root); - root.addChild("New Specimen Request"); - } - } - - @RequiresPermission(RequestSpecimensPermission.class) - public class ShowAPICreateSpecimenRequestAction extends SimpleViewAction - { - @Override - public ModelAndView getView(CreateSpecimenRequestForm form, BindException errors) throws Exception - { - return getCreateSpecimenRequestView(form, errors); - } - - @Override - public void addNavTrail(NavTree root) - { - addSpecimenRequestsNavTrail(root); - root.addChild("New Specimen Request"); - } - } - - private ActionURL getManageRequestURL(int requestID, ActionURL returnUrl) - { - ActionURL url = urlProvider(SpecimenUrls.class).getManageRequestURL(getContainer()); - url.addParameter(IdForm.PARAMS.id, Integer.toString(requestID)); - if (returnUrl != null) - url.addReturnURL(returnUrl); - return url; - } - - private ActionURL getExtendedRequestURL(int requestID, String returnUrl) - { - ActionURL url = new ActionURL(ExtendedSpecimenRequestAction.class, getContainer()); - url.addParameter(IdForm.PARAMS.id, Integer.toString(requestID)); -// if (returnUrl != null) -// url.addReturnURL(returnUrl); - return url; - } - - @RequiresPermission(RequestSpecimensPermission.class) - public class ExtendedSpecimenRequestAction extends SimpleViewAction - { - @Override - public ModelAndView getView(CreateSpecimenRequestForm form, BindException errors) - { - VBox vbox = new VBox(); - - ExtendedSpecimenRequestView view = SpecimenManager.get().getExtendedSpecimenRequestView(getViewContext()); - if (view != null && view.isActive()) - { - HtmlView requestView = new HtmlView(view.getBody()); - vbox.addView(requestView); - } - else - { - vbox.addView(new HtmlView("An extended specimen request view has not been provided for this folder.")); - } - return vbox; - } - - @Override - public void addNavTrail(NavTree root) - { - addSpecimenRequestsNavTrail(root); - root.addChild("Extended Specimen Request"); - } - } -} diff --git a/specimen/src/org/labkey/specimen/actions/SpecimenEventBean.java b/specimen/src/org/labkey/specimen/actions/SpecimenEventBean.java new file mode 100644 index 00000000..9eb62970 --- /dev/null +++ b/specimen/src/org/labkey/specimen/actions/SpecimenEventBean.java @@ -0,0 +1,25 @@ +package org.labkey.specimen.actions; + +import org.labkey.api.action.ReturnUrlForm; +import org.labkey.api.specimen.Vial; + +public class SpecimenEventBean extends ReturnUrlForm +{ + private Vial _vial; + + public SpecimenEventBean(Vial vial, String returnUrl) + { + _vial = vial; + setReturnUrl(returnUrl); + } + + public Vial getVial() + { + return _vial; + } + + public void setVial(Vial vial) + { + _vial = vial; + } +} diff --git a/specimen/src/org/labkey/specimen/actions/SpecimenHeaderBean.java b/specimen/src/org/labkey/specimen/actions/SpecimenHeaderBean.java new file mode 100644 index 00000000..3b01b1d2 --- /dev/null +++ b/specimen/src/org/labkey/specimen/actions/SpecimenHeaderBean.java @@ -0,0 +1,143 @@ +package org.labkey.specimen.actions; + +import org.labkey.api.data.ColumnInfo; +import org.labkey.api.data.TableInfo; +import org.labkey.api.query.FieldKey; +import org.labkey.api.query.QueryService; +import org.labkey.api.specimen.SpecimenQuerySchema; +import org.labkey.api.specimen.query.SpecimenQueryView; +import org.labkey.api.study.Study; +import org.labkey.api.study.StudyService; +import org.labkey.api.util.Pair; +import org.labkey.api.view.ActionURL; +import org.labkey.api.view.NotFoundException; +import org.labkey.api.view.ViewContext; + +import java.util.Collections; +import java.util.Iterator; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +public final class SpecimenHeaderBean +{ + private final ActionURL _otherViewURL; + private final ViewContext _viewContext; + private final boolean _showingVials; + private final Set> _filteredPtidVisits; + + private Integer _selectedRequest; + + public SpecimenHeaderBean(ViewContext context, SpecimenQueryView view) + { + this(context, view, Collections.emptySet()); + } + + public SpecimenHeaderBean(ViewContext context, SpecimenQueryView view, Set> filteredPtidVisits) throws RuntimeException + { + Map params = context.getRequest().getParameterMap(); + + String currentTable = view.isShowingVials() ? "SpecimenDetail" : "SpecimenSummary"; + String otherTable = view.isShowingVials() ? "SpecimenSummary" : "SpecimenDetail"; + ActionURL otherView = context.cloneActionURL(); + otherView.deleteParameters(); + + Study study = StudyService.get().getStudy(context.getContainer()); + if (null == study) + throw new NotFoundException("No study exists in this folder."); + SpecimenQuerySchema schema = SpecimenQuerySchema.get(study, context.getUser()); + + TableInfo otherTableInfo = schema.getTable(otherTable); + + for (Map.Entry param : params.entrySet()) + { + int dotIndex = param.getKey().indexOf('.'); + + if (dotIndex >= 0) + { + String table = param.getKey().substring(0, dotIndex); + String columnClause = param.getKey().substring(dotIndex + 1); + String[] columnClauseParts = columnClause.split("~"); + String column = columnClauseParts[0]; + + if (table.equals(currentTable)) + { + // use the query service to check to see if the current filter column is present + // in the other view. If so, we'll add a filter parameter with the same value on the + // other view. Otherwise, we'll keep the parameter, but we won't map it to the other view: + boolean translatable = column.equals("sort"); + + if (!translatable) + { + Map presentCols = QueryService.get().getColumns(otherTableInfo, + Collections.singleton(FieldKey.fromString(column))); + translatable = !presentCols.isEmpty(); + } + + if (translatable) + { + String key = otherTable + "." + columnClause; + otherView.addParameter(key, param.getValue()[0]); + continue; + } + } + + if (table.equals(currentTable) || table.equals(otherTable)) + otherView.addParameter(param.getKey(), param.getValue()[0]); + } + } + + otherView.replaceParameter("showVials", Boolean.toString(!view.isShowingVials())); + if (null != params.get(SpecimenQueryView.PARAMS.excludeRequestedBySite.name())) + otherView.replaceParameter(SpecimenQueryView.PARAMS.excludeRequestedBySite.name(), + params.get(SpecimenQueryView.PARAMS.excludeRequestedBySite.name())[0]); + _otherViewURL = otherView; + _viewContext = context; + _showingVials = view.isShowingVials(); + _filteredPtidVisits = filteredPtidVisits; + } + + public Integer getSelectedRequest() + { + return _selectedRequest; + } + + public void setSelectedRequest(Integer selectedRequest) + { + _selectedRequest = selectedRequest; + } + + public ActionURL getOtherViewURL() + { + return _otherViewURL; + } + + public ViewContext getViewContext() + { + return _viewContext; + } + + public boolean isShowingVials() + { + return _showingVials; + } + + public Set> getFilteredPtidVisits() + { + return _filteredPtidVisits; + } + + public boolean isSingleVisitFilter() + { + if (getFilteredPtidVisits().isEmpty()) + return false; + Iterator> visitIt = getFilteredPtidVisits().iterator(); + String firstVisit = visitIt.next().getValue(); + while (visitIt.hasNext()) + { + if (!Objects.equals(firstVisit, visitIt.next().getValue())) + return false; + } + return true; + } +} diff --git a/specimen/src/org/labkey/specimen/actions/SpecimenViewTypeForm.java b/specimen/src/org/labkey/specimen/actions/SpecimenViewTypeForm.java new file mode 100644 index 00000000..cfff8bf5 --- /dev/null +++ b/specimen/src/org/labkey/specimen/actions/SpecimenViewTypeForm.java @@ -0,0 +1,42 @@ +package org.labkey.specimen.actions; + +import org.labkey.api.action.QueryViewAction; +import org.labkey.api.specimen.query.SpecimenQueryView; + +public class SpecimenViewTypeForm extends QueryViewAction.QueryExportForm +{ + public enum PARAMS + { + showVials, + viewMode + } + + private boolean _showVials; + private SpecimenQueryView.Mode _viewMode = SpecimenQueryView.Mode.DEFAULT; + + public boolean isShowVials() + { + return _showVials; + } + + public void setShowVials(boolean showVials) + { + _showVials = showVials; + } + + public String getViewMode() + { + return _viewMode.name(); + } + + public SpecimenQueryView.Mode getViewModeEnum() + { + return _viewMode; + } + + public void setViewMode(String viewMode) + { + if (viewMode != null) + _viewMode = SpecimenQueryView.Mode.valueOf(viewMode); + } +} diff --git a/specimen/src/org/labkey/specimen/actions/SpecimensViewBean.java b/specimen/src/org/labkey/specimen/actions/SpecimensViewBean.java new file mode 100644 index 00000000..9c93d066 --- /dev/null +++ b/specimen/src/org/labkey/specimen/actions/SpecimensViewBean.java @@ -0,0 +1,37 @@ +package org.labkey.specimen.actions; + +import org.labkey.api.specimen.Vial; +import org.labkey.api.specimen.query.SpecimenQueryView; +import org.labkey.api.view.ViewContext; + +import java.util.List; + +public abstract class SpecimensViewBean +{ + protected SpecimenQueryView _specimenQueryView; + protected List _vials; + + public SpecimensViewBean(ViewContext context, List vials, boolean showHistoryLinks, + boolean showRecordSelectors, boolean disableLowVialIndicators, boolean restrictRecordSelectors) + { + _vials = vials; + if (vials != null && vials.size() > 0) + { + _specimenQueryView = SpecimenQueryView.createView(context, vials, SpecimenQueryView.ViewType.VIALS); + _specimenQueryView.setShowHistoryLinks(showHistoryLinks); + _specimenQueryView.setShowRecordSelectors(showRecordSelectors); + _specimenQueryView.setDisableLowVialIndicators(disableLowVialIndicators); + _specimenQueryView.setRestrictRecordSelectors(restrictRecordSelectors); + } + } + + public SpecimenQueryView getSpecimenQueryView() + { + return _specimenQueryView; + } + + public List getVials() + { + return _vials; + } +} diff --git a/specimen/src/org/labkey/specimen/actions/UpdateSpecimenCommentsBean.java b/specimen/src/org/labkey/specimen/actions/UpdateSpecimenCommentsBean.java new file mode 100644 index 00000000..4a4259a1 --- /dev/null +++ b/specimen/src/org/labkey/specimen/actions/UpdateSpecimenCommentsBean.java @@ -0,0 +1,157 @@ +package org.labkey.specimen.actions; + +import org.apache.commons.lang3.time.DateUtils; +import org.jetbrains.annotations.NotNull; +import org.labkey.api.annotations.Migrate; +import org.labkey.api.data.Container; +import org.labkey.api.specimen.SpecimenManager; +import org.labkey.api.specimen.Vial; +import org.labkey.api.specimen.model.SpecimenComment; +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.Visit; +import org.labkey.api.study.model.VisitService; +import org.labkey.api.util.PageFlowUtil; +import org.labkey.api.view.RedirectException; +import org.labkey.api.view.ViewContext; + +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.TreeMap; + +public class UpdateSpecimenCommentsBean extends SpecimensViewBean +{ + private final String _referrer; + private final String _currentComment; + private final boolean _mixedComments; + private final boolean _currentFlagState; + private final boolean _mixedFlagState; + private final Map> _participantVisitMap; + + public UpdateSpecimenCommentsBean(ViewContext context, List vials, String referrer) + { + super(context, vials, false, false, true, true); + _referrer = referrer; + Map currentComments = SpecimenManager.get().getSpecimenComments(vials); + boolean mixedComments = false; + boolean mixedFlagState = false; + SpecimenComment prevComment = currentComments.get(vials.get(0)); + + for (int i = 1; i < vials.size() && (!mixedFlagState || !mixedComments); i++) + { + SpecimenComment comment = currentComments.get(vials.get(i)); + + // a missing comment indicates a 'false' for history conflict: + boolean currentFlagState = comment != null && comment.isQualityControlFlag(); + boolean previousFlagState = prevComment != null && prevComment.isQualityControlFlag(); + mixedFlagState = mixedFlagState || currentFlagState != previousFlagState; + String currentCommentString = comment != null ? comment.getComment() : null; + String previousCommentString = prevComment != null ? prevComment.getComment() : null; + mixedComments = mixedComments || !Objects.equals(previousCommentString, currentCommentString); + prevComment = comment; + } + + _currentComment = !mixedComments && prevComment != null ? prevComment.getComment() : null; + _currentFlagState = mixedFlagState || (prevComment != null && prevComment.isQualityControlFlag()); + _mixedComments = mixedComments; + _mixedFlagState = mixedFlagState; + _participantVisitMap = generateParticipantVisitMap(vials, getStudyRedirectIfNull(context.getContainer())); + } + + @Migrate // Use SpecimenController2.getStudyRedirectIfNull() + @NotNull + private static Study getStudyRedirectIfNull(Container c) + { + Study study = StudyService.get().getStudy(c); + if (null == study) + { + // redirect to the study home page, where admins will see a 'create study' button, + // and non-admins will simply see a message that no study exists. + throw new RedirectException(PageFlowUtil.urlProvider(StudyUrls.class).getBeginURL(c)); + } + return study; + } + + protected Map> generateParticipantVisitMap(List vials, Study study) + { + Map> pvMap = new TreeMap<>(); + + if (TimepointType.CONTINUOUS == study.getTimepointType()) + return Collections.emptyMap(); + + boolean isDateStudy = TimepointType.DATE == study.getTimepointType(); + Date startDate = isDateStudy ? study.getStartDate() : new Date(); + + for (Vial vial : vials) + { + Double visit; + if (isDateStudy) + if (null != vial.getDrawTimestamp()) + visit = Double.valueOf((vial.getDrawTimestamp().getTime() - startDate.getTime()) / DateUtils.MILLIS_PER_DAY); + else + visit = null; + else + visit = vial.getVisitValue(); + + if (visit != null) + { + String ptid = vial.getPtid(); + Visit v = getVisitForSequence(study, visit); + if (ptid != null && v != null) + { + if (!pvMap.containsKey(ptid)) + pvMap.put(ptid, new HashMap<>()); + pvMap.get(ptid).put(v.getDisplayString(), vial.getRowId()); + } + } + } + return pvMap; + } + + public String getReferrer() + { + return _referrer; + } + + public String getCurrentComment() + { + return _currentComment; + } + + public boolean isMixedComments() + { + return _mixedComments; + } + + public boolean isCurrentFlagState() + { + return _currentFlagState; + } + + public boolean isMixedFlagState() + { + return _mixedFlagState; + } + + public Map> getParticipantVisitMap() + { + return _participantVisitMap; + } + + public static Visit getVisitForSequence(Study study, double seqNum) + { + List visits = VisitService.get().getVisits(study, Visit.Order.SEQUENCE_NUM); + for (Visit v : visits) + { + if (seqNum >= v.getSequenceNumMinDouble() && seqNum <= v.getSequenceNumMaxDouble()) + return v; + } + return null; + } +} diff --git a/specimen/src/org/labkey/specimen/notifications/ActorNotificationRecipientSet.java b/specimen/src/org/labkey/specimen/notifications/ActorNotificationRecipientSet.java new file mode 100644 index 00000000..07289a6e --- /dev/null +++ b/specimen/src/org/labkey/specimen/notifications/ActorNotificationRecipientSet.java @@ -0,0 +1,126 @@ +/* + * 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.specimen.notifications; + +import org.labkey.api.data.Container; +import org.labkey.api.security.User; +import org.labkey.api.specimen.location.LocationImpl; +import org.labkey.api.specimen.location.LocationManager; +import org.labkey.api.specimen.model.SpecimenRequestActor; +import org.labkey.api.specimen.notifications.NotificationRecipientSet; +import org.labkey.api.specimen.requirements.SpecimenRequestRequirementProvider; +import org.labkey.api.util.HtmlString; +import org.labkey.api.util.HtmlStringBuilder; +import org.labkey.api.util.PageFlowUtil; +import org.labkey.api.util.SafeToRender; +import org.labkey.api.view.ActionURL; +import org.labkey.specimen.actions.ShowGroupMembersAction; + +/** + * User: brittp + * Date: May 4, 2007 + * Time: 3:36:19 PM + */ +public class ActorNotificationRecipientSet extends NotificationRecipientSet +{ + private final SpecimenRequestActor _actor; + private final LocationImpl _location; + + public ActorNotificationRecipientSet(SpecimenRequestActor actor, LocationImpl location) + { + super(); + _actor = actor; + _location = location; + + User[] users = actor.getMembers(location); + String[] addresses = new String[users.length]; + boolean[] addressInactive = new boolean[users.length]; + for (int i = 0; i < users.length; i++) + { + addresses[i] = users[i].getEmail(); + addressInactive[i] = !users[i].isActive(); + } + + setEmailAddresses(addresses, addressInactive); + } + + public SpecimenRequestActor getActor() + { + return _actor; + } + + public LocationImpl getLocation() + { + return _location; + } + + @Override + public String getShortRecipientDescription() + { + StringBuilder shortDesc = new StringBuilder(); + shortDesc.append(getActor().getLabel()); + if (getLocation() != null) + shortDesc.append(", ").append(getLocation().getDisplayName()); + return shortDesc.toString(); + } + + @Override + public String getLongRecipientDescription() + { + StringBuilder emailList = new StringBuilder(getShortRecipientDescription()); + emailList.append(": "); + emailList.append(getEmailAddressesAsString(", ")); + return emailList.toString(); + } + + public String getFormValue() + { + return getActor().getRowId() + "," + (getLocation() != null ? getLocation().getRowId() : "-1"); + } + + public static ActorNotificationRecipientSet getFromFormValue(Container container, String formValue) + { + String[] ids = formValue.split(","); + int actorId = Integer.parseInt(ids[0]); + int locationId = Integer.parseInt(ids[1]); + SpecimenRequestActor actor = SpecimenRequestRequirementProvider.get().getActor(container, actorId); + LocationImpl location = locationId >= 0 ? LocationManager.get().getLocation(container, locationId) : null; + return new ActorNotificationRecipientSet(actor, location); + } + + private HtmlString getConfigureEmailsLinkHTML(ActionURL currentUrl) + { + ActionURL url = ShowGroupMembersAction.getShowGroupMembersURL( + getActor().getContainer(), + getActor().getRowId(), + getLocation() != null ? getLocation().getRowId() : null, + currentUrl + ); + return PageFlowUtil.link("Configure Addresses").href(url).getHtmlString(); + } + + public SafeToRender getHtmlDescriptionAndLink(boolean hasEmailAddresses, ActionURL currentUrl) + { + HtmlStringBuilder builder = HtmlStringBuilder.of(getShortRecipientDescription()); + if (hasEmailAddresses) + builder.append(HtmlString.unsafe(PageFlowUtil.helpPopup("Group Members", getEmailAddressesAsString("
") + "
" + + getConfigureEmailsLinkHTML(currentUrl), true))); + else + builder.append(" ").append(getConfigureEmailsLinkHTML(currentUrl)); + return builder; + } +} diff --git a/specimen/src/org/labkey/specimen/pipeline/SpecimenPipeline.java b/specimen/src/org/labkey/specimen/pipeline/SpecimenPipeline.java index 2919c888..babbc742 100644 --- a/specimen/src/org/labkey/specimen/pipeline/SpecimenPipeline.java +++ b/specimen/src/org/labkey/specimen/pipeline/SpecimenPipeline.java @@ -10,7 +10,7 @@ import org.labkey.api.study.SpecimenTransform; import org.labkey.api.study.StudyService; import org.labkey.api.view.ViewContext; -import org.labkey.specimen.actions.SpecimenController2.ImportSpecimenDataAction; +import org.labkey.specimen.actions.SpecimenController.ImportSpecimenDataAction; import java.io.File; diff --git a/specimen/src/org/labkey/specimen/query/SpecimenRequestQueryView.java b/specimen/src/org/labkey/specimen/query/SpecimenRequestQueryView.java index 484ec1c3..506424f7 100644 --- a/specimen/src/org/labkey/specimen/query/SpecimenRequestQueryView.java +++ b/specimen/src/org/labkey/specimen/query/SpecimenRequestQueryView.java @@ -35,7 +35,6 @@ import org.labkey.api.specimen.security.permissions.ManageRequestsPermission; import org.labkey.api.specimen.security.permissions.RequestSpecimensPermission; import org.labkey.api.specimen.settings.SettingsManager; -import org.labkey.api.study.SpecimenUrls; import org.labkey.api.study.Study; import org.labkey.api.study.StudyService; import org.labkey.api.study.StudyUtils; @@ -43,6 +42,9 @@ import org.labkey.api.view.ActionURL; import org.labkey.api.view.NavTree; import org.labkey.api.view.ViewContext; +import org.labkey.specimen.actions.SpecimenController.DeleteRequestAction; +import org.labkey.specimen.actions.SpecimenController.ManageRequestAction; +import org.labkey.specimen.actions.SpecimenController.SubmitRequestAction; import java.io.IOException; import java.io.Writer; @@ -124,15 +126,16 @@ public void renderGridCellContents(RenderContext ctx, Writer out) throws IOExcep if (statusId.intValue() == _shoppingCartStatusRowId.intValue() && canEdit) { - ActionURL submitUrl = PageFlowUtil.urlProvider(SpecimenUrls.class).getSubmitRequestURL(ctx.getContainer(), "${requestId}"); - ActionURL cancelUrl = PageFlowUtil.urlProvider(SpecimenUrls.class).getDeleteRequestURL(ctx.getContainer(), "${requestId}"); + ActionURL submitUrl = new ActionURL(SubmitRequestAction.class, ctx.getContainer()).addParameter("id", "${requestId}");; + ActionURL cancelUrl = new ActionURL(DeleteRequestAction.class, ctx.getContainer()).addParameter("id", "${requestId}");; content.append(PageFlowUtil.button("Submit").href(submitUrl).usePost(StudyUtils.SUBMISSION_WARNING)); content.append(PageFlowUtil.button("Cancel").href(cancelUrl).usePost(StudyUtils.CANCELLATION_WARNING)); } } - ActionURL detailsUrl = PageFlowUtil.urlProvider(SpecimenUrls.class).getRequestDetailsURL(ctx.getContainer(), "${requestId}"); + ActionURL detailsUrl = new ActionURL(ManageRequestAction.class, ctx.getContainer()).addParameter("id", "${requestId}"); + content.append(PageFlowUtil.button("Details").href(detailsUrl)); } content.append(""); diff --git a/specimen/src/org/labkey/specimen/report/SpecimenTypeVisitReport.java b/specimen/src/org/labkey/specimen/report/SpecimenTypeVisitReport.java index a71c4c68..961e135d 100644 --- a/specimen/src/org/labkey/specimen/report/SpecimenTypeVisitReport.java +++ b/specimen/src/org/labkey/specimen/report/SpecimenTypeVisitReport.java @@ -16,12 +16,12 @@ package org.labkey.specimen.report; import org.labkey.api.data.SimpleFilter; -import org.labkey.api.study.SpecimenUrls; import org.labkey.api.study.Visit; import org.labkey.api.util.DemoMode; import org.labkey.api.util.Formats; import org.labkey.api.util.PageFlowUtil; import org.labkey.api.view.ActionURL; +import org.labkey.specimen.actions.SpecimenController; import org.labkey.specimen.actions.SpecimenReportActions; import java.util.Collection; @@ -113,7 +113,7 @@ protected String getCellHtml(Visit visit, SummaryByVisitType summary) { if (summary == null || summary.getVialCount() == null) return " "; - ActionURL link = PageFlowUtil.urlProvider(SpecimenUrls.class).getSpecimensURL(_container, true); + ActionURL link = SpecimenController.getSpecimensURL(_container, true); link = updateURLFilterParameter(link, "SpecimenDetail.Visit/SequenceNumMin", visit.getSequenceNumMinDouble()); link = updateURLFilterParameter(link, "SpecimenDetail.PrimaryType/Description", summary.getPrimaryType()); diff --git a/specimen/src/org/labkey/specimen/report/SpecimenVisitReportAction.java b/specimen/src/org/labkey/specimen/report/SpecimenVisitReportAction.java index cbd660ca..c3a4e99c 100644 --- a/specimen/src/org/labkey/specimen/report/SpecimenVisitReportAction.java +++ b/specimen/src/org/labkey/specimen/report/SpecimenVisitReportAction.java @@ -7,8 +7,8 @@ import org.labkey.api.view.NavTree; import org.labkey.api.view.VBox; import org.labkey.specimen.actions.ReportConfigurationBean; -import org.labkey.specimen.actions.SpecimenController2; -import org.labkey.specimen.actions.SpecimenController2.AutoReportListAction; +import org.labkey.specimen.actions.SpecimenController; +import org.labkey.specimen.actions.SpecimenController.AutoReportListAction; import org.springframework.validation.BindException; import org.springframework.web.servlet.ModelAndView; @@ -52,7 +52,7 @@ public ModelAndView getView(FormType specimenVisitReportForm, BindException erro public void addNavTrail(NavTree root) { Container c = getContainer(); - ActionURL overviewURL = new ActionURL(SpecimenController2.OverviewAction.class, c); + ActionURL overviewURL = new ActionURL(SpecimenController.OverviewAction.class, c); root.addChild("Specimen Overview", overviewURL); root.addChild("Available Reports", new ActionURL(AutoReportListAction.class, c)); root.addChild("Specimen Report: " + _form.getLabel()); diff --git a/specimen/src/org/labkey/specimen/report/SpecimenVisitReportParameters.java b/specimen/src/org/labkey/specimen/report/SpecimenVisitReportParameters.java index f94232db..a4487420 100644 --- a/specimen/src/org/labkey/specimen/report/SpecimenVisitReportParameters.java +++ b/specimen/src/org/labkey/specimen/report/SpecimenVisitReportParameters.java @@ -40,7 +40,7 @@ import org.labkey.api.view.ActionURL; import org.labkey.api.view.UnauthorizedException; import org.labkey.api.view.ViewForm; -import org.labkey.specimen.actions.SpecimenController2.CompleteSpecimenAction; +import org.labkey.specimen.actions.SpecimenController.CompleteSpecimenAction; import java.io.IOException; import java.io.StringWriter; diff --git a/specimen/src/org/labkey/specimen/report/participant/ParticipantVisitReport.java b/specimen/src/org/labkey/specimen/report/participant/ParticipantVisitReport.java index 12c7df16..8422fbf1 100644 --- a/specimen/src/org/labkey/specimen/report/participant/ParticipantVisitReport.java +++ b/specimen/src/org/labkey/specimen/report/participant/ParticipantVisitReport.java @@ -17,12 +17,11 @@ import org.labkey.api.data.SimpleFilter; import org.labkey.api.study.CohortFilter; -import org.labkey.api.study.SpecimenUrls; import org.labkey.api.study.StudyService; import org.labkey.api.study.Visit; import org.labkey.api.util.DemoMode; -import org.labkey.api.util.PageFlowUtil; import org.labkey.api.view.ActionURL; +import org.labkey.specimen.actions.SpecimenController; import org.labkey.specimen.report.SpecimenReportManager; import org.labkey.specimen.report.SpecimenReportTitle; import org.labkey.specimen.report.SpecimenVisitReport; @@ -109,7 +108,7 @@ protected String getCellHtml(Visit visit, SummaryByVisitParticipant summary) { if (summary == null || summary.getVialCount() == null) return " "; - ActionURL link = PageFlowUtil.urlProvider(SpecimenUrls.class).getSpecimensURL(_container, true); + ActionURL link = SpecimenController.getSpecimensURL(_container, true); link = updateURLFilterParameter(link, "SpecimenDetail.Visit/SequenceNumMin", visit.getSequenceNumMinDouble()); link = updateURLFilterParameter(link, "SpecimenDetail." + StudyService.get().getSubjectColumnName(getContainer()), summary.getParticipantId()); String linkHtml = link.getLocalURIString(); diff --git a/specimen/src/org/labkey/specimen/report/request/RequestParticipantReport.java b/specimen/src/org/labkey/specimen/report/request/RequestParticipantReport.java index 90ae82d4..7a9f383d 100644 --- a/specimen/src/org/labkey/specimen/report/request/RequestParticipantReport.java +++ b/specimen/src/org/labkey/specimen/report/request/RequestParticipantReport.java @@ -17,10 +17,9 @@ import org.labkey.api.data.SimpleFilter; import org.labkey.api.specimen.query.SpecimenQueryView; -import org.labkey.api.study.SpecimenUrls; import org.labkey.api.study.Visit; -import org.labkey.api.util.PageFlowUtil; import org.labkey.api.view.ActionURL; +import org.labkey.specimen.actions.SpecimenController; import org.labkey.specimen.report.RequestSummaryByVisitType; import org.labkey.specimen.report.SpecimenReportManager; import org.labkey.specimen.report.SpecimenTypeLevel; @@ -96,7 +95,7 @@ protected String getCellHtml(Visit visit, RequestSummaryByVisitType summary) { if (summary == null || summary.getVialCount() == null) return " "; - ActionURL url = PageFlowUtil.urlProvider(SpecimenUrls.class).getSpecimensURL(_container, true); + ActionURL url = SpecimenController.getSpecimensURL(_container, true); url = updateURLFilterParameter(url, "SpecimenDetail.Visit/SequenceNumMin", visit.getSequenceNumMinDouble()); url = updateURLFilterParameter(url, "SpecimenDetail.PrimaryType/Description", summary.getPrimaryType()); SpecimenTypeLevel level = getTypeLevelEnum(); diff --git a/specimen/src/org/labkey/specimen/security/permissions/LockSpecimensPermission.java b/specimen/src/org/labkey/specimen/security/permissions/LockSpecimensPermission.java index e0ee6960..e94d0697 100644 --- a/specimen/src/org/labkey/specimen/security/permissions/LockSpecimensPermission.java +++ b/specimen/src/org/labkey/specimen/security/permissions/LockSpecimensPermission.java @@ -16,7 +16,7 @@ package org.labkey.specimen.security.permissions; import org.labkey.api.security.permissions.AbstractPermission; -import org.labkey.api.study.StudyService; +import org.labkey.specimen.SpecimenModule; /* * User: Dave @@ -29,6 +29,6 @@ public LockSpecimensPermission() { super("Lock Specimens", "Allows locking of specimens", - StudyService.get().getStudyModuleClass()); // TODO: Switch to SpecimenModule.class + SpecimenModule.class); } } \ No newline at end of file diff --git a/specimen/src/org/labkey/specimen/security/permissions/ManageDisplaySettingsPermission.java b/specimen/src/org/labkey/specimen/security/permissions/ManageDisplaySettingsPermission.java index 5730b142..77415037 100644 --- a/specimen/src/org/labkey/specimen/security/permissions/ManageDisplaySettingsPermission.java +++ b/specimen/src/org/labkey/specimen/security/permissions/ManageDisplaySettingsPermission.java @@ -16,7 +16,7 @@ package org.labkey.specimen.security.permissions; import org.labkey.api.security.permissions.AbstractPermission; -import org.labkey.api.study.StudyService; +import org.labkey.specimen.SpecimenModule; /* * User: Dave @@ -29,6 +29,6 @@ public ManageDisplaySettingsPermission() { super("Manage Specimen Display Settings", "Allows management of display settings on the specimen request form", - StudyService.get().getStudyModuleClass()); // TODO: Switch to SpecimenModule.class + SpecimenModule.class); } } \ No newline at end of file diff --git a/specimen/src/org/labkey/specimen/security/permissions/ManageNewRequestFormPermission.java b/specimen/src/org/labkey/specimen/security/permissions/ManageNewRequestFormPermission.java new file mode 100644 index 00000000..1523cc94 --- /dev/null +++ b/specimen/src/org/labkey/specimen/security/permissions/ManageNewRequestFormPermission.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2009-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.specimen.security.permissions; + +import org.labkey.api.security.permissions.AbstractPermission; +import org.labkey.specimen.SpecimenModule; + +/* +* User: Dave +* Date: May 18, 2009 +* Time: 11:27:04 AM +*/ +public class ManageNewRequestFormPermission extends AbstractPermission +{ + public ManageNewRequestFormPermission() + { + super("Manage New Request Form", + "Allows management of the form shown during a new specimen request", + SpecimenModule.class); + } +} \ No newline at end of file diff --git a/specimen/src/org/labkey/specimen/security/permissions/ManageNotificationsPermission.java b/specimen/src/org/labkey/specimen/security/permissions/ManageNotificationsPermission.java index b6b9f66e..095df7f7 100644 --- a/specimen/src/org/labkey/specimen/security/permissions/ManageNotificationsPermission.java +++ b/specimen/src/org/labkey/specimen/security/permissions/ManageNotificationsPermission.java @@ -16,7 +16,7 @@ package org.labkey.specimen.security.permissions; import org.labkey.api.security.permissions.AbstractPermission; -import org.labkey.api.study.StudyService; +import org.labkey.specimen.SpecimenModule; /* * User: Dave @@ -29,6 +29,6 @@ public ManageNotificationsPermission() { super("Manage Notifications", "Allows management of specimen request notifications", - StudyService.get().getStudyModuleClass()); // TODO: Switch to SpecimenModule.class + SpecimenModule.class); } } \ No newline at end of file diff --git a/specimen/src/org/labkey/specimen/security/permissions/ManageRequestRequirementsPermission.java b/specimen/src/org/labkey/specimen/security/permissions/ManageRequestRequirementsPermission.java index a8a33655..4fcdffd1 100644 --- a/specimen/src/org/labkey/specimen/security/permissions/ManageRequestRequirementsPermission.java +++ b/specimen/src/org/labkey/specimen/security/permissions/ManageRequestRequirementsPermission.java @@ -16,7 +16,7 @@ package org.labkey.specimen.security.permissions; import org.labkey.api.security.permissions.AbstractPermission; -import org.labkey.api.study.StudyService; +import org.labkey.specimen.SpecimenModule; /* * User: Dave @@ -29,6 +29,6 @@ public ManageRequestRequirementsPermission() { super("Manage Specimen Request Default Requirements", "Allows management of specimen request default requirements", - StudyService.get().getStudyModuleClass()); // TODO: Switch to SpecimenModule.class + SpecimenModule.class); } } \ No newline at end of file diff --git a/specimen/src/org/labkey/specimen/security/permissions/ManageRequestStatusesPermission.java b/specimen/src/org/labkey/specimen/security/permissions/ManageRequestStatusesPermission.java new file mode 100644 index 00000000..a4dc555d --- /dev/null +++ b/specimen/src/org/labkey/specimen/security/permissions/ManageRequestStatusesPermission.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2009-2012 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.specimen.security.permissions; + +import org.labkey.api.security.permissions.AbstractPermission; +import org.labkey.specimen.SpecimenModule; + +/* +* User: Dave +* Date: May 14, 2009 +* Time: 4:47:59 PM +*/ +public class ManageRequestStatusesPermission extends AbstractPermission +{ + public ManageRequestStatusesPermission() + { + super("Manage Request Statuses", "Allows management of specimen request statuses", + SpecimenModule.class); + } +} \ No newline at end of file diff --git a/specimen/src/org/labkey/specimen/security/permissions/ManageSpecimenActorsPermission.java b/specimen/src/org/labkey/specimen/security/permissions/ManageSpecimenActorsPermission.java new file mode 100644 index 00000000..a69196b3 --- /dev/null +++ b/specimen/src/org/labkey/specimen/security/permissions/ManageSpecimenActorsPermission.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2009-2012 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.specimen.security.permissions; + +import org.labkey.api.security.permissions.AbstractPermission; +import org.labkey.specimen.SpecimenModule; + +/* +* User: Dave +* Date: May 13, 2009 +* Time: 3:22:10 PM +*/ +public class ManageSpecimenActorsPermission extends AbstractPermission +{ + public ManageSpecimenActorsPermission() + { + super("Manage Specimen Actors", "Allows management of specimen actors", + SpecimenModule.class); + } +} \ No newline at end of file diff --git a/specimen/src/org/labkey/specimen/security/permissions/SetSpecimenCommentsPermission.java b/specimen/src/org/labkey/specimen/security/permissions/SetSpecimenCommentsPermission.java new file mode 100644 index 00000000..29b16bb3 --- /dev/null +++ b/specimen/src/org/labkey/specimen/security/permissions/SetSpecimenCommentsPermission.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2009-2012 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.specimen.security.permissions; + +import org.labkey.api.security.permissions.AbstractPermission; +import org.labkey.specimen.SpecimenModule; + +/* +* User: Dave +* Date: May 18, 2009 +* Time: 11:39:16 AM +*/ +public class SetSpecimenCommentsPermission extends AbstractPermission +{ + public SetSpecimenCommentsPermission() + { + super("Set Specimen Comments", + "Allows the setting of comments on specimens", + SpecimenModule.class); + } +} \ No newline at end of file diff --git a/specimen/src/org/labkey/specimen/security/roles/SpecimenCoordinatorRole.java b/specimen/src/org/labkey/specimen/security/roles/SpecimenCoordinatorRole.java index f2bf9f3b..845c69f1 100644 --- a/specimen/src/org/labkey/specimen/security/roles/SpecimenCoordinatorRole.java +++ b/specimen/src/org/labkey/specimen/security/roles/SpecimenCoordinatorRole.java @@ -19,18 +19,18 @@ import org.labkey.api.security.SecurityManager; import org.labkey.api.security.permissions.EditSharedViewPermission; import org.labkey.api.specimen.security.permissions.EditSpecimenDataPermission; -import org.labkey.api.specimen.security.permissions.ManageNewRequestFormPermission; import org.labkey.api.specimen.security.permissions.ManageRequestSettingsPermission; -import org.labkey.api.specimen.security.permissions.ManageRequestStatusesPermission; import org.labkey.api.specimen.security.permissions.ManageRequestsPermission; -import org.labkey.api.specimen.security.permissions.ManageSpecimenActorsPermission; import org.labkey.api.specimen.security.permissions.RequestSpecimensPermission; -import org.labkey.api.specimen.security.permissions.SetSpecimenCommentsPermission; +import org.labkey.specimen.security.permissions.SetSpecimenCommentsPermission; import org.labkey.api.study.security.permissions.ManageStudyPermission; import org.labkey.specimen.security.permissions.LockSpecimensPermission; import org.labkey.specimen.security.permissions.ManageDisplaySettingsPermission; +import org.labkey.specimen.security.permissions.ManageNewRequestFormPermission; import org.labkey.specimen.security.permissions.ManageNotificationsPermission; import org.labkey.specimen.security.permissions.ManageRequestRequirementsPermission; +import org.labkey.specimen.security.permissions.ManageRequestStatusesPermission; +import org.labkey.specimen.security.permissions.ManageSpecimenActorsPermission; /* * User: Dave diff --git a/specimen/src/org/labkey/specimen/view/ManageSpecimenView.java b/specimen/src/org/labkey/specimen/view/ManageSpecimenView.java new file mode 100644 index 00000000..2be73ebb --- /dev/null +++ b/specimen/src/org/labkey/specimen/view/ManageSpecimenView.java @@ -0,0 +1,11 @@ +package org.labkey.specimen.view; + +import org.labkey.api.view.JspView; + +public class ManageSpecimenView extends JspView +{ + public ManageSpecimenView() + { + super("/org/labkey/specimen/view/manageSpecimens.jsp"); + } +} diff --git a/specimen/src/org/labkey/specimen/view/SpecimenToolsWebPartFactory.java b/specimen/src/org/labkey/specimen/view/SpecimenToolsWebPartFactory.java index cbf45ce5..e71c081d 100644 --- a/specimen/src/org/labkey/specimen/view/SpecimenToolsWebPartFactory.java +++ b/specimen/src/org/labkey/specimen/view/SpecimenToolsWebPartFactory.java @@ -11,8 +11,8 @@ import org.labkey.api.view.ViewContext; import org.labkey.api.view.WebPartFactory; import org.labkey.specimen.actions.ShowSearchAction; -import org.labkey.specimen.actions.SpecimenController2.AutoReportListAction; -import org.labkey.specimen.actions.SpecimenController2.ShowCreateSpecimenRequestAction; +import org.labkey.specimen.actions.SpecimenController.AutoReportListAction; +import org.labkey.specimen.actions.SpecimenController.ShowCreateSpecimenRequestAction; import java.util.ArrayList; import java.util.List; @@ -34,8 +34,7 @@ protected List getItems(ViewContext portalCtx) String iconBase = portalCtx.getContextPath() + "/study/tools/"; List items = new ArrayList<>(); - ActionURL vialSearchURL = new ActionURL(ShowSearchAction.class, portalCtx.getContainer()); - vialSearchURL.addParameter("showVials", true); + ActionURL vialSearchURL = ShowSearchAction.getShowSearchURL(portalCtx.getContainer(), true); items.add(new StudyToolsWebPart.Item("Vial Search", iconBase + "specimen_search.png", vialSearchURL)); if (SettingsManager.get().isSpecimenRequestEnabled(portalCtx.getContainer())) diff --git a/specimen/src/org/labkey/specimen/view/SpecimenWebPart.java b/specimen/src/org/labkey/specimen/view/SpecimenWebPart.java new file mode 100644 index 00000000..b7e02c2e --- /dev/null +++ b/specimen/src/org/labkey/specimen/view/SpecimenWebPart.java @@ -0,0 +1,64 @@ +/* + * 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.specimen.view; + +import org.labkey.api.specimen.settings.SettingsManager; +import org.labkey.api.study.Study; +import org.labkey.api.view.JspView; + +/** + * User: Mark Igra + * Date: Jul 28, 2006 + * Time: 10:50:05 AM + */ +public class SpecimenWebPart extends JspView +{ + @SuppressWarnings("unused") // Invoked via reflection + public SpecimenWebPart() + { + this(false, null); + } + + public SpecimenWebPart(boolean wide, Study study) + { + super("/org/labkey/specimen/view/webPart.jsp", new SpecimenWebPartBean(wide, + (null != study) && SettingsManager.get().getRepositorySettings(study.getContainer()).isSpecimenDataEditable())); + setTitle("Specimens"); + } + + public static class SpecimenWebPartBean + { + private final boolean _wide; + private final boolean _isEditableSpecimens; + + public SpecimenWebPartBean(boolean wide, boolean isEditableSpecimens) + { + _wide = wide; + _isEditableSpecimens = isEditableSpecimens; + } + + public boolean isWide() + { + return _wide; + } + + public boolean isEditableSpecimens() + { + return _isEditableSpecimens; + } + } +} diff --git a/specimen/src/org/labkey/specimen/view/SpecimenWebPartFactory.java b/specimen/src/org/labkey/specimen/view/SpecimenWebPartFactory.java index e542d241..3630d1ff 100644 --- a/specimen/src/org/labkey/specimen/view/SpecimenWebPartFactory.java +++ b/specimen/src/org/labkey/specimen/view/SpecimenWebPartFactory.java @@ -2,7 +2,6 @@ import org.jetbrains.annotations.NotNull; import org.labkey.api.security.permissions.ReadPermission; -import org.labkey.api.specimen.view.SpecimenWebPart; import org.labkey.api.study.Study; import org.labkey.api.study.StudyService; import org.labkey.api.view.DefaultWebPartFactory; diff --git a/specimen/src/org/labkey/specimen/view/chooseImporter.jsp b/specimen/src/org/labkey/specimen/view/chooseImporter.jsp new file mode 100644 index 00000000..9496310b --- /dev/null +++ b/specimen/src/org/labkey/specimen/view/chooseImporter.jsp @@ -0,0 +1,161 @@ +<% +/* + * Copyright (c) 2013-2016 LabKey Corporation. All rights reserved. No portion of this work may be reproduced in + * any form or by any electronic or mechanical means without written permission from LabKey Corporation. + */ +%> +<%@ page import="com.google.common.collect.Iterables" %> +<%@ page import="org.labkey.api.admin.AdminUrls" %> +<%@ 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.study.SpecimenService" %> +<%@ page import="org.labkey.api.study.SpecimenTransform" %> +<%@ page import="org.labkey.api.study.StudyUrls" %> +<%@ page import="org.labkey.api.util.Button" %> +<%@ page import="org.labkey.api.util.HtmlString" %> +<%@ page import="org.labkey.api.util.URLHelper" %> +<%@ page import="org.labkey.api.view.ActionURL" %> +<%@ page import="org.labkey.specimen.actions.ShowUploadSpecimensAction" %> +<%@ page import="java.util.Collection" %> +<%@ page import="static org.labkey.api.util.HtmlString.NBSP" %> +<%@ page extends="org.labkey.api.jsp.JspBase" %> +<%@ taglib prefix="labkey" uri="http://www.labkey.org/taglib" %> +<% + Container c = getContainer(); + User user = getUser(); + Collection specimenTransforms = SpecimenService.get().getSpecimenTransforms(c); + specimenTransforms.removeIf(transform -> null == transform.getManageAction(c, user)); + + URLHelper cancelLink = getActionURL().getReturnURL(); + if (cancelLink == null) + cancelLink = urlProvider(StudyUrls.class).getManageStudyURL(getContainer()); + int numberOfTransforms = specimenTransforms.size(); + int rowNumber = 0; + + String selected = SpecimenService.get().getActiveSpecimenImporter(c); + HtmlString manageFoldersLink = h(urlProvider(AdminUrls.class).getFolderTypeURL(c)); + HtmlString labkeyEditionsLink = h("https://www.labkey.com/products-services/labkey-server/labkey-server-editions-feature-comparison/"); + HtmlString contactUsLink = h("https://www.labkey.com/about/contact/"); + HtmlString manuallyImportSpecimensLink = h(urlFor(ShowUploadSpecimensAction.class)); +%> + + + + + +
+ <% if (numberOfTransforms > 1) { %> +

+ Activate automatic import of specimen data from an external source on this page. In order to prevent automated reloads from overwriting specimen data upon manual or scheduled imports, only one specimen import mechanism may be active at a time for a given container. +

+ <% + } + %> +

+ Learn more about <%=helpLink("externalSpecimens", "Automated External Specimen Imports")%>. +

+ +
+ + +

Configure Specimen Import

+ + <% if (numberOfTransforms > 1) { %> + + + + + + + + + <% + for (SpecimenTransform transform : specimenTransforms) + { + ActionURL manageAction = transform.getManageAction(c, user); + HtmlString transformName = h(transform.getName()); + + %> + + + + + + <% + } + %> +
NameActive
<%=transformName%> +
+ + > +
+
<%=link("configure", manageAction)%>
+ +
+ + <%= new Button.ButtonBuilder("Save") + .submit(true) + .build() + %> + + <%= new Button.ButtonBuilder("Cancel") + .href(cancelLink) + .build() + %> +
+ + <% } else if (numberOfTransforms == 1) { %> + <% + SpecimenTransform transform = Iterables.get(specimenTransforms, 0); + ActionURL manageAction = transform.getManageAction(c, user); + %> + +
+ + + + + + +
<%=h(transform.getName())%><%=link("configure", manageAction)%>
+ +

+ + <%= new Button.ButtonBuilder("Done") + .href(cancelLink) + .build() + %> + + <% } else { %> + + <% if (ModuleLoader.getInstance().hasModule("professional")) { %> +
+ External Specimen Import is not currently available for this folder. + To use External import, > enable the Professional Module for this folder. +
+ <% } else { %> +
+

<%=NBSP%>

+ External Specimen Import is a Premium LabKey feature. > Learn more or + > contact LabKey . +
+ <% + } + %> + + > Import specimens manually + <% + } + %> +
+
diff --git a/specimen/src/org/labkey/specimen/view/configRequestabilityRules.jsp b/specimen/src/org/labkey/specimen/view/configRequestabilityRules.jsp index f75162be..c47cf257 100644 --- a/specimen/src/org/labkey/specimen/view/configRequestabilityRules.jsp +++ b/specimen/src/org/labkey/specimen/view/configRequestabilityRules.jsp @@ -424,7 +424,7 @@ Ext.Msg.wait("Saving..."); Ext.Ajax.request({ - url : LABKEY.ActionURL.buildURL("specimen2", "updateRequestabilityRules"), + url : LABKEY.ActionURL.buildURL("specimen", "updateRequestabilityRules"), method : 'POST', success: saveComplete, failure: LABKEY.Utils.getCallbackWrapper(LABKEY.Utils.displayAjaxErrorResponse, this, true), diff --git a/specimen/src/org/labkey/specimen/view/importSpecimens.jsp b/specimen/src/org/labkey/specimen/view/importSpecimens.jsp index da369aed..60a5546e 100644 --- a/specimen/src/org/labkey/specimen/view/importSpecimens.jsp +++ b/specimen/src/org/labkey/specimen/view/importSpecimens.jsp @@ -18,8 +18,8 @@ <%@ taglib prefix="labkey" uri="http://www.labkey.org/taglib" %> <%@ page import="org.labkey.api.view.HttpView"%> <%@ page import="org.labkey.api.view.JspView"%> -<%@ page import="org.labkey.specimen.actions.SpecimenController2.ImportSpecimensBean"%> -<%@ page import="org.labkey.specimen.actions.SpecimenController2.SubmitSpecimenBatchImportAction" %> +<%@ page import="org.labkey.specimen.actions.SpecimenController.ImportSpecimensBean"%> +<%@ page import="org.labkey.specimen.actions.SpecimenController.SubmitSpecimenBatchImportAction" %> <%@ page import="org.labkey.specimen.pipeline.SpecimenArchive" %> <%@ page import="java.util.List" %> <%@ page import="java.util.zip.ZipException" %> diff --git a/specimen/src/org/labkey/specimen/view/labSpecimenLists.jsp b/specimen/src/org/labkey/specimen/view/labSpecimenLists.jsp new file mode 100644 index 00000000..3e2dca9d --- /dev/null +++ b/specimen/src/org/labkey/specimen/view/labSpecimenLists.jsp @@ -0,0 +1,198 @@ +<% +/* + * 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.specimen.Vial"%> +<%@ page import="org.labkey.api.specimen.location.LocationImpl"%> +<%@ 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.specimen.actions.SpecimenController" %> +<%@ page import="org.labkey.specimen.actions.SpecimenController.DownloadSpecimenListAction" %> +<%@ page import="org.labkey.specimen.actions.SpecimenController.EmailLabSpecimenListsAction" %> +<%@ page import="org.labkey.specimen.actions.SpecimenController.LabSpecimenListsBean" %> +<%@ page import="org.labkey.specimen.notifications.ActorNotificationRecipientSet" %> +<%@ taglib prefix="labkey" uri="http://www.labkey.org/taglib" %> +<%@ page extends="org.labkey.api.jsp.JspBase" %> +<% + JspView me = (JspView) HttpView.currentView(); + LabSpecimenListsBean bean = me.getModelBean(); + boolean originating = bean.getType() == LabSpecimenListsBean.Type.ORIGINATING; +%> + + + + + + <% + if (!bean.isRequirementsComplete()) + { + %> + + + + <% + } + %> + + + + + + + <% + int rowCount = 0; + for (LocationImpl location : bean.getLabs()) + { + ActionURL downloadURL = urlFor(DownloadSpecimenListAction.class) + .addParameter("id", bean.getSpecimenRequest().getRowId()) + .addParameter("destSiteId", bean.getSpecimenRequest().getDestinationSiteId()) + .addParameter("listType", bean.getType().toString()) + .addParameter("sourceSiteId", location.getRowId()); + %> + + + + + + + <% + } + %> +
+ + WARNING: The requirements for this request are incomplete. + +
<%= text(originating ? "Originating" : "Providing") %> LocationDownload optionsSpecimen IDsEmail Recipients
<%= h(location.getDisplayName()) %> + + + + + + + +
<%=link("Export to Excel").href(downloadURL.replaceParameter("export", "xls"))%>
<%=link("Export to text file").href(downloadURL.replaceParameter("export", "tsv")) %>
+
+
+ + <% + for (Vial vial : bean.getSpecimens(location)) + { + %> + + + + <% + } + %> +
<%= h(vial.getGlobalUniqueId()) %>
+
+ + <% + boolean hasInactiveEmailAddress = false; + for (ActorNotificationRecipientSet possibleNotification : bean.getPossibleNotifications()) + { + LocationImpl notifyLocation = possibleNotification.getLocation(); + if (notifyLocation != null && + notifyLocation.getRowId() != bean.getSpecimenRequest().getDestinationSiteId() && + notifyLocation.getRowId() != location.getRowId()) + continue; + + boolean hasEmailAddresses = possibleNotification.getAllEmailAddresses().length > 0; + if (hasEmailAddresses) + hasInactiveEmailAddress |= possibleNotification.hasInactiveEmailAddress(); + + %> + + + + + + <% + } + if (hasInactiveEmailAddress) + { + %> + + + + + <% + } + %> +
+ > + + <%=possibleNotification.getHtmlDescriptionAndLink(hasEmailAddresses, getActionURL())%> + + <%= text(notifyLocation != null && notifyLocation.getRowId() == bean.getSpecimenRequest().getDestinationSiteId() ? "Requesting Location " : "") %> + <%= text(notifyLocation != null && notifyLocation.getRowId() == location.getRowId() ? bean.getType().getDisplay() + " Location" : "") %> +
+ + + Include inactive users
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
  + Any comments will be included in all notifications that are sent. Additionally, they
+ will appear in the request history. +
Comments
  + Selecting an attachment format will automatically attach appropriate specimen lists to
+ any notifications. It is not necessary to attach the lists manually.
Attachment Format(s) + Excel
+ Text
+
  + Supporting documents are optional. If added, they will be available to all notification
+ recipients, as well as in the request history.
Supporting
Documents
+
+
+
+
+ +
 <%= button("Send Email").submit(true) %> <%= button("Cancel").href(SpecimenController.getManageRequestURL(getContainer(), bean.getSpecimenRequest().getRowId(), null))%>
+
\ No newline at end of file diff --git a/specimen/src/org/labkey/specimen/view/manageActorOrder.jsp b/specimen/src/org/labkey/specimen/view/manageActorOrder.jsp new file mode 100644 index 00000000..25a5e585 --- /dev/null +++ b/specimen/src/org/labkey/specimen/view/manageActorOrder.jsp @@ -0,0 +1,104 @@ +<% +/* + * 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.specimen.model.SpecimenRequestActor"%> +<%@ page import="org.labkey.api.specimen.requirements.SpecimenRequestRequirementProvider"%> +<%@ page import="org.labkey.specimen.actions.SpecimenController.ManageActorOrderAction" %> +<%@ page import="org.labkey.specimen.actions.SpecimenController.ManageActorsAction" %> +<%@ taglib prefix="labkey" uri="http://www.labkey.org/taglib" %> +<%@ page extends="org.labkey.api.jsp.JspBase" %> +<% + SpecimenRequestActor[] actors = SpecimenRequestRequirementProvider.get().getActors(getContainer()); +%> + + + + + + + + + +
+ <% + %> + + + <%= button("Move Up").submit(true).onClick("return orderModule(0)") %>

+ <%= button("Move Down").submit(true).onClick("return orderModule(1)") %> +
+ + <%= button("Save").submit(true) %> <%= button("Cancel").href(urlFor(ManageActorsAction.class)) %> +
diff --git a/specimen/src/org/labkey/specimen/view/manageActors.jsp b/specimen/src/org/labkey/specimen/view/manageActors.jsp new file mode 100644 index 00000000..8ef5bfe2 --- /dev/null +++ b/specimen/src/org/labkey/specimen/view/manageActors.jsp @@ -0,0 +1,143 @@ +<% +/* + * 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.specimen.location.LocationImpl" %> +<%@ page import="org.labkey.api.specimen.location.LocationManager" %> +<%@ page import="org.labkey.api.specimen.model.SpecimenRequestActor" %> +<%@ page import="org.labkey.api.specimen.requirements.SpecimenRequestRequirementProvider" %> +<%@ page import="org.labkey.api.study.Study"%> +<%@ 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.specimen.actions.ShowGroupMembersAction" %> +<%@ page import="org.labkey.specimen.actions.SpecimenController.DeleteActorAction" %> +<%@ page import="org.labkey.specimen.actions.SpecimenController.ManageActorOrderAction" %> +<%@ page import="org.labkey.specimen.actions.SpecimenController.ManageActorsAction" %> +<%@ page import="java.util.Set" %> +<%@ taglib prefix="labkey" uri="http://www.labkey.org/taglib" %> +<%@ page extends="org.labkey.api.jsp.JspBase" %> +<% + JspView me = (JspView) HttpView.currentView(); + Study study = me.getModelBean(); + SpecimenRequestActor[] actors = SpecimenRequestRequirementProvider.get().getActors(getContainer()); + String showMemberSitesIdStr = request.getParameter("showMemberSites"); + int showMemberSitesId = -1; + Set inUseActorIds = SpecimenRequestRequirementProvider.get().getActorsInUseSet(getContainer()); + if (showMemberSitesIdStr != null && showMemberSitesIdStr.length() > 0) + { + try + { + showMemberSitesId = Integer.parseInt(showMemberSitesIdStr); + } + catch (NumberFormatException e) + { + // fall through + } + } +%> + + + + + + + + + + <% + if (actors != null && actors.length > 0) + { + for (SpecimenRequestActor actor : actors) + { + ActionURL updateMembersLink = null; + if (actor.isPerSite()) + { + if (showMemberSitesId != actor.getRowId()) + updateMembersLink = urlFor(ManageActorsAction.class).addParameter("showMemberSites", actor.getRowId()); + } + else + updateMembersLink = ShowGroupMembersAction.getShowGroupMembersURL(getContainer(), actor.getRowId(), null, null); + %> + + + + + + + <% + } + } + %> + + + + + + + + + + +
 Actor NameActor Type 
  + + + <%=h(actor.isPerSite() ? "Multiple Per Study (Location Affiliated)" : "One Per Study")%> + <% + if (showMemberSitesId == actor.getRowId()) + { + if (study.getLocations().size() > 0) + { + %> + Choose Site:
+ <% + for (LocationImpl location : LocationManager.get().getLocations(study.getContainer())) + { + %><%= h(location.getDisplayName()) %>
<% + } + } + else + { + %> + No Sites Configured + <% + } + } + else + { + %><%= link("Update Members").href(updateMembersLink) %><% + if (!inUseActorIds.contains(actor.getRowId())) + { + %><%=link("Delete").onClick("return LABKEY.Utils.confirmAndPost('Deleting this actor will delete all information about its membership. All member emails will need to be entered again if you recreate this actor.', '" + h(urlFor(DeleteActorAction.class).addParameter("id", actor.getRowId())) + "')") %><% + } + } + %> +
New Actor: + +
  + <%= button("Save").submit(true) %>  + <%= button("Done").submit(true).onClick("document.manageActors.nextPage.value=''; return true;") %> + <%= button("Cancel").href(urlProvider(StudyUrls.class).getManageStudyURL(study.getContainer())) %>  + <%= button("Change Order").submit(true).onClick("document.manageActors.nextPage.value=" + q(urlFor(ManageActorOrderAction.class)) + "; return true;") %> + +  
+
\ No newline at end of file diff --git a/specimen/src/org/labkey/specimen/view/manageComments.jsp b/specimen/src/org/labkey/specimen/view/manageComments.jsp new file mode 100644 index 00000000..35b3001a --- /dev/null +++ b/specimen/src/org/labkey/specimen/view/manageComments.jsp @@ -0,0 +1,215 @@ +<% +/* + * Copyright (c) 2009-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. + */ +%> +<%@ page import="org.labkey.api.exp.OntologyManager" %> +<%@ page import="org.labkey.api.exp.PropertyDescriptor" %> +<%@ page import="org.labkey.api.exp.PropertyType" %> +<%@ page import="org.labkey.api.study.Dataset" %> +<%@ page import="org.labkey.api.study.Study" %> +<%@ page import="org.labkey.api.study.StudyInternalService" %> +<%@ 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.view.HttpView" %> +<%@ page import="org.labkey.api.view.JspView" %> +<%@ page import="org.labkey.specimen.actions.SpecimenController.ManageCommentsForm" %> +<%@ page import="org.labkey.specimen.actions.SpecimenController.ManageSpecimenCommentsAction" %> +<%@ page import="java.util.Collections" %> +<%@ page import="java.util.List" %> +<%@ page extends="org.labkey.api.jsp.JspBase" %> +<%@ taglib prefix="labkey" uri="http://www.labkey.org/taglib" %> +<% + JspView me = (JspView) HttpView.currentView(); + ManageCommentsForm bean = me.getModelBean(); + + Study study = StudyService.get().getStudy(getContainer()); + + List datasets = StudyInternalService.get().getDatasets(study); + + List ptidDescriptors = Collections.emptyList(); + List ptidVisitDescriptors = Collections.emptyList(); + Integer participantCommentDatasetId = bean.getParticipantCommentDatasetId(); + Integer participantVisitCommentDatasetId = bean.getParticipantVisitCommentDatasetId(); + + if (participantCommentDatasetId != null && participantCommentDatasetId >= 0) + { + Dataset dataset = StudyService.get().getDataset(study.getContainer(), participantCommentDatasetId); + if (dataset != null) + ptidDescriptors = OntologyManager.getPropertiesForType(dataset.getTypeURI(), study.getContainer()); + } + + if (study.getTimepointType() != TimepointType.CONTINUOUS) + { + if (participantVisitCommentDatasetId != null && participantVisitCommentDatasetId >= 0) + { + Dataset dataset = StudyService.get().getDataset(study.getContainer(), participantVisitCommentDatasetId); + if (dataset != null) + ptidVisitDescriptors = OntologyManager.getPropertiesForType(dataset.getTypeURI(), study.getContainer()); + } + } + + String subjectNounSingle = StudyService.get().getSubjectNounSingular(getContainer()); +%> + + + + + + + + + + + + +
Note: Only users with read access to the selected dataset(s) will be able to view comment + information. +
+
+ The comments associated with each <%= h(subjectNounSingle) %> or <%= h(subjectNounSingle) %>/Visit are + saved as fields in datasets. + Each of the datasets can contain multiple fields, but only one field can + be designated to hold the comment text. Comment fields must be of type text or multi-line text. + Comments will appear automatically in columns for the specimen and vial views. +
+ + + + +
<%=h(subjectNounSingle)%> Comment Assignment
+ + + + + + + + + +
Comment + Dataset<%= helpPopup(subjectNounSingle + "/Comment Dataset", "Comments can be associated with each " + + subjectNounSingle.toLowerCase() + ". The dataset selected must be a demographics dataset.")%> + + +
Comment Field Name + +
+ +
<%=h(subjectNounSingle)%>/Visit Comment Assignment
+ <% + + if (study.getTimepointType() == TimepointType.CONTINUOUS) + { + %> + Not available in continuous date-based studies. + <% + } + else + { + %> + + + + + + + + + + + + +
Comment + Dataset<%= helpPopup(subjectNounSingle + "/Comment Dataset", "Comments can be associated with each " + + subjectNounSingle.toLowerCase() + "/visit combination. The dataset selected cannot be a demographics dataset.")%> + + +
Comment Field Name + +
+
+ <%= button("Save").submit(true) %> + <%= button("Cancel").href(urlProvider(StudyUrls.class).getManageStudyURL(getContainer())) %> +
+ <% + } + %> +
\ No newline at end of file diff --git a/specimen/src/org/labkey/specimen/view/manageDefaultReqs.jsp b/specimen/src/org/labkey/specimen/view/manageDefaultReqs.jsp index aa36e0a3..9f27408e 100644 --- a/specimen/src/org/labkey/specimen/view/manageDefaultReqs.jsp +++ b/specimen/src/org/labkey/specimen/view/manageDefaultReqs.jsp @@ -23,8 +23,8 @@ <%@ page import="org.labkey.api.view.HttpView" %> <%@ page import="org.labkey.api.view.JspView" %> <%@ page import="org.labkey.specimen.actions.ManageReqsBean" %> -<%@ page import="org.labkey.specimen.actions.SpecimenController2" %> -<%@ page import="org.labkey.specimen.actions.SpecimenController2.ManageDefaultReqsAction" %> +<%@ page import="org.labkey.specimen.actions.SpecimenController" %> +<%@ page import="org.labkey.specimen.actions.SpecimenController.ManageDefaultReqsAction" %> <%@ taglib prefix="labkey" uri="http://www.labkey.org/taglib" %> <%@ page extends="org.labkey.api.jsp.JspBase" %> <% @@ -35,7 +35,7 @@ SpecimenRequestRequirement[] receiverRequirements = bean.getReceiverRequirements(); SpecimenRequestRequirement[] generalRequirements = bean.getGeneralRequirements(); SpecimenRequestActor[] actors = bean.getActors(); - ActionURL deleteDefaultRequirement = urlFor(SpecimenController2.DeleteDefaultRequirementAction.class).addParameter("id",0); + ActionURL deleteDefaultRequirement = urlFor(SpecimenController.DeleteDefaultRequirementAction.class).addParameter("id",0); %> diff --git a/specimen/src/org/labkey/specimen/view/manageNotifications.jsp b/specimen/src/org/labkey/specimen/view/manageNotifications.jsp index a09adcd2..2ec66bf6 100644 --- a/specimen/src/org/labkey/specimen/view/manageNotifications.jsp +++ b/specimen/src/org/labkey/specimen/view/manageNotifications.jsp @@ -30,7 +30,7 @@ <%@ 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.specimen.actions.SpecimenController2.ManageNotificationsAction" %> +<%@ page import="org.labkey.specimen.actions.SpecimenController.ManageNotificationsAction" %> <%@ page extends="org.labkey.api.jsp.JspBase" %> <%@ taglib prefix="labkey" uri="http://www.labkey.org/taglib" %> <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> diff --git a/specimen/src/org/labkey/specimen/view/manageRepositorySettings.jsp b/specimen/src/org/labkey/specimen/view/manageRepositorySettings.jsp index a685dc39..68877562 100644 --- a/specimen/src/org/labkey/specimen/view/manageRepositorySettings.jsp +++ b/specimen/src/org/labkey/specimen/view/manageRepositorySettings.jsp @@ -20,7 +20,7 @@ <%@ page import="org.labkey.api.study.Study" %> <%@ page import="org.labkey.api.study.StudyService" %> <%@ page import="org.labkey.api.view.ActionURL" %> -<%@ page import="org.labkey.specimen.actions.SpecimenController2.ManageRepositorySettingsAction" %> +<%@ page import="org.labkey.specimen.actions.SpecimenController.ManageRepositorySettingsAction" %> <%@ taglib prefix="labkey" uri="http://www.labkey.org/taglib" %> <%@ page extends="org.labkey.api.jsp.JspBase" %> <% diff --git a/specimen/src/org/labkey/specimen/view/manageRequest.jsp b/specimen/src/org/labkey/specimen/view/manageRequest.jsp new file mode 100644 index 00000000..dbfeef87 --- /dev/null +++ b/specimen/src/org/labkey/specimen/view/manageRequest.jsp @@ -0,0 +1,544 @@ +<% +/* + * 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.json.JSONArray"%> +<%@ page import="org.labkey.api.data.Container" %> +<%@ page import="org.labkey.api.data.ContainerManager"%> +<%@ page import="org.labkey.api.security.User"%> +<%@ page import="org.labkey.api.security.UserManager"%> +<%@ page import="org.labkey.api.settings.AppProps"%> +<%@ page import="org.labkey.api.specimen.SpecimenManager" %> +<%@ page import="org.labkey.api.specimen.SpecimenRequestManager" %> +<%@ page import="org.labkey.api.specimen.SpecimenRequestStatus"%> +<%@ page import="org.labkey.api.specimen.Vial" %> +<%@ page import="org.labkey.api.specimen.location.LocationImpl" %> +<%@ page import="org.labkey.api.specimen.location.LocationManager" %> +<%@ page import="org.labkey.api.specimen.model.SpecimenRequestActor" %> +<%@ page import="org.labkey.api.specimen.requirements.SpecimenRequestRequirement" %> +<%@ page import="org.labkey.api.specimen.requirements.SpecimenRequestRequirementProvider" %> +<%@ page import="org.labkey.api.specimen.settings.SettingsManager" %> +<%@ page import="org.labkey.api.study.Location" %> +<%@ page import="org.labkey.api.study.SpecimenService" %> +<%@ page import="org.labkey.api.study.StudyUtils" %> +<%@ page import="org.labkey.api.util.HtmlString" %> +<%@ page import="org.labkey.api.util.SafeToRender" %> +<%@ 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.specimen.actions.ManageRequestBean" %> +<%@ page import="org.labkey.specimen.actions.ShowSearchAction" %> +<%@ page import="org.labkey.specimen.actions.SpecimenController" %> +<%@ page import="org.labkey.specimen.actions.SpecimenController.DeleteMissingRequestSpecimensAction" %> +<%@ page import="org.labkey.specimen.actions.SpecimenController.DeleteRequestAction" %> +<%@ page import="org.labkey.specimen.actions.SpecimenController.ExtendedSpecimenRequestAction" %> +<%@ page import="org.labkey.specimen.actions.SpecimenController.ImportVialIdsAction" %> +<%@ page import="org.labkey.specimen.actions.SpecimenController.LabSpecimenListsAction" %> +<%@ page import="org.labkey.specimen.actions.SpecimenController.LabSpecimenListsBean" %> +<%@ page import="org.labkey.specimen.actions.SpecimenController.ManageRequestAction" %> +<%@ page import="org.labkey.specimen.actions.SpecimenController.ManageRequirementAction" %> +<%@ page import="org.labkey.specimen.actions.SpecimenController.RequestHistoryAction" %> +<%@ page import="org.labkey.specimen.actions.SpecimenController.SubmitRequestAction" %> +<%@ page import="java.util.Arrays" %> +<%@ page import="java.util.List" %> +<%@ taglib prefix="labkey" uri="http://www.labkey.org/taglib" %> +<%@ page extends="org.labkey.api.jsp.JspBase" %> +<%! + @Override + public void addClientDependencies(ClientDependencies dependencies) + { + dependencies.add("clientapi/ext3"); + dependencies.add("FileUploadField.js"); + dependencies.add("study/StudyWizard.js"); + } +%> +<% + JspView me = (JspView) HttpView.currentView(); + Container c = getContainer(); + User user = getUser(); + ManageRequestBean bean = me.getModelBean(); + String comments = bean.getSpecimenRequest().getComments(); + if (comments == null) + comments = "[No description provided]"; + boolean hasExtendedRequestView = SpecimenManager.get().getExtendedSpecimenRequestView(getViewContext()) != null; + SpecimenRequestActor[] actors = SpecimenRequestRequirementProvider.get().getActors(c); + SpecimenRequestRequirement[] requirements = SpecimenRequestManager.get().getRequestRequirements(bean.getSpecimenRequest()); + Location destinationLocation = bean.getDestinationSite(); + User creatingUser = UserManager.getUser(bean.getSpecimenRequest().getCreatedBy()); + List locations = LocationManager.get().getLocations(c); + boolean notYetSubmitted = false; + if (SettingsManager.get().isSpecimenShoppingCartEnabled(c)) + { + SpecimenRequestStatus cartStatus = SpecimenRequestManager.get().getRequestShoppingCartStatus(c, user); + notYetSubmitted = bean.getSpecimenRequest().getStatusId() == cartStatus.getRowId(); + } + + SafeToRender specimenSearchButton = SpecimenRequestManager.get().hasEditRequestPermissions(user, bean.getSpecimenRequest()) ? + button("Specimen Search").href(ShowSearchAction.getShowSearchURL(getContainer(), true)) : HtmlString.EMPTY_STRING; + SafeToRender importVialIdsButton = SpecimenRequestManager.get().hasEditRequestPermissions(user, bean.getSpecimenRequest()) ? + button("Upload Specimen Ids").href(urlFor(ImportVialIdsAction.class).addParameter("id", bean.getSpecimenRequest().getRowId())) : HtmlString.EMPTY_STRING; + + String availableStudyName = ContainerManager.getAvailableChildContainerName(c, "New Study"); +%> + + +<% + if (bean.isSuccessfulSubmission()) + { +%> +

Your request has been successfully submitted.

+<% + } + %> + +<% + if (!SpecimenService.get().getRequestCustomizer().hideRequestWarnings()) { %> + <% + boolean multipleSites = bean.getProvidingLocations().length > 1; + if (bean.hasMissingSpecimens() || multipleSites) + { + %> + + + + <% + if (bean.hasMissingSpecimens()) + { + %> + + + + <% + } + + if (multipleSites && !bean.isFinalState()) + { + %> + + + + <% + } + } + if (bean.isRequestManager() && (bean.isFinalState() || bean.isRequirementsComplete())) + { + %> + + + + + + +<% } + } + if (notYetSubmitted) + { +%> + + + + + + +<% + } + else + { +%> + Only the request creator (<%= h(creatingUser.getDisplayName(user)) %>) or an administrator may submit or cancel this request. +<% + } + } + else // is submitted + { %> + <%= SpecimenService.get().getRequestCustomizer().getSubmittedMessage(getContainer(), bean.getSpecimenRequest().getRowId()) %><% + } +%> + + + + + + + + + + + + + + + + + + +<% + if (null == bean.getSpecimenQueryView()) + { +%> + + + +<% + } +%> +
Request Warnings
+ WARNING: Missing Specimens

+ The following specimen(s) are part of this request, but have been deleted from the database:
+ <% + for (String specId : bean.getMissingSpecimens()) + { + %><%= h(specId) %>
<% + } + + if (bean.isRequestManager()) + { + %> +
You may remove these specimens from this request if they are not expected to be re-added to the database.
+ <%= button("Delete missing specimens") + .onClick("return LABKEY.Utils.confirmAndPost('Delete missing specimens? This action cannot be undone.', '" + h(urlFor(DeleteMissingRequestSpecimensAction.class).addParameter("id", bean.getSpecimenRequest().getRowId())) + "')") %><% + } + %> +
+ WARNING: Vials are at multiple locations.
+ Requests containing vials from multiple providing locations may require increased processing time.
+ Multiple locations are expected if some vials have already shipped while others have not. + Current locations for this request are:
+ <% + for (Location location : bean.getProvidingLocations()) + { + %><%= h(location.getLabel()) %>
<% + } + %> +
Request Notes
+ <% + if (bean.isFinalState()) + { + %> + This request is in a final state; no changes are allowed.
+ To make changes, you must + change the request's status to a non-final state. + <% + } + else if (bean.isRequirementsComplete()) + { + %> + This request's requirements are complete. Next steps include:
+
    +
  • Email specimen lists to their originating locations: <%= link("Originating Location Specimen Lists", + urlFor(LabSpecimenListsAction.class) + .addParameter("id", bean.getSpecimenRequest().getRowId()) + .addParameter("listType", LabSpecimenListsBean.Type.ORIGINATING.toString())) %> +
  • +
  • Email specimen lists to their providing locations: <%= link("Providing Location Specimen Lists", + urlFor(LabSpecimenListsAction.class) + .addParameter("id", bean.getSpecimenRequest().getRowId()) + .addParameter("listType", LabSpecimenListsBean.Type.PROVIDING.toString())) %> +
  • +
  • Update request status to indicate completion: <%= link("Update Request", + SpecimenController.getManageRequestStatusURL(getContainer(), bean.getSpecimenRequest().getRowId())) %> +
  • +
+ <% + } + %> +
Unsubmitted Request
This request has not been submitted yet. +<% + if (SpecimenRequestManager.get().hasEditRequestPermissions(user, bean.getSpecimenRequest())) + { +%> +
+<% + List vials = bean.getSpecimenRequest().getVials(); + if (!vials.isEmpty() || SpecimenService.get().getRequestCustomizer().allowEmptyRequests()) + { +%> + Request processing will begin after the request has been submitted.

+ <%= button("Submit Request") + .href(urlFor(SubmitRequestAction.class).addParameter("id", bean.getSpecimenRequest().getRowId())) + .onClick("return LABKEY.Utils.confirmAndPost('" + StudyUtils.SUBMISSION_WARNING + "', '" + h(urlFor(SubmitRequestAction.class).addParameter("id", bean.getSpecimenRequest().getRowId())) + "')") %> +<% + } + else + { +%> + You must add specimens before submitting your request.

+ <%=specimenSearchButton%> + <%=importVialIdsButton%> +<% + } + + if (AppProps.getInstance().isExperimentalFeatureEnabled(SpecimenService.CREATE_SPECIMEN_STUDY)) + { +%> <%= button("Create Study").href("javascript:void(0)").onClick("showNewStudyWizard();") %> +<% + } +%> + <%= button("Cancel Request") + .onClick("return LABKEY.Utils.confirmAndPost('" + StudyUtils.CANCELLATION_WARNING + "', '" + h(urlFor(DeleteRequestAction.class).addParameter("id", bean.getSpecimenRequest().getRowId())) + "')") %> +<% + if (bean.getReturnUrl() != null) + { +%> + <%= button("Return to Specimen View").href(bean.getReturnUrl()) %> +<% + } +%> +
+
Request Information
+ + + + + + + + + + + + + + + + + + + + + +
Requester<%= h(creatingUser != null ? creatingUser.getDisplayName(user) : "Unknown") %>
Requesting Location<%= h(destinationLocation != null ? destinationLocation.getDisplayName() : "Not specified") %>
Request Date<%=formatDateTime(bean.getSpecimenRequest().getCreated())%>
Description<%=HtmlString.unsafe(h(comments).toString().replaceAll("\\n", "
\n"))%>
Status<%= h(bean.getStatus().getLabel()) %>
+
+ <%= link("View History", urlFor(RequestHistoryAction.class).addParameter("id", bean.getSpecimenRequest().getRowId())) %>  + <%= bean.isRequestManager() ? link("Update Request", SpecimenController.getManageRequestStatusURL(getContainer(), bean.getSpecimenRequest().getRowId())) : HtmlString.EMPTY_STRING %> + <% + if (hasExtendedRequestView) + { + %> + <%= bean.isRequestManager() ? link("Update Extended Request", urlFor(ExtendedSpecimenRequestAction.class).addParameter("id", bean.getSpecimenRequest().getRowId())) : HtmlString.EMPTY_STRING %> + <% + } + %> + <%= bean.isRequestManager() ? link("Originating Location Specimen Lists", + new ActionURL(LabSpecimenListsAction.class, c) + .addParameter("id", bean.getSpecimenRequest().getRowId()) + .addParameter("listType", LabSpecimenListsBean.Type.ORIGINATING.toString())) : HtmlString.EMPTY_STRING %> + <%= bean.isRequestManager() ? link("Providing Location Specimen Lists", + new ActionURL(LabSpecimenListsAction.class, c) + .addParameter("id", bean.getSpecimenRequest().getRowId()) + .addParameter("listType", LabSpecimenListsBean.Type.PROVIDING.toString())) : HtmlString.EMPTY_STRING %> +
Current Requirements
+ <% + if (!bean.isRequestManager() && requirements.length == 0) + { + %> + No requirements are associated with this request. + <% + } + else + { + %> + + + + + + + + + <% + for (SpecimenRequestRequirement requirement : requirements) + { + Location location = requirement.getLocation(); + String siteLabel; + if (location == null) + siteLabel = "N/A"; + else if (location.getLabel() == null) + siteLabel = ""; + else + siteLabel = location.getDisplayName(); + %> + + + + + + + + <% + } + if (bean.isRequestManager() && !bean.isFinalState()) + { + %> + + + + + + + <% + } + %> +
ActorLocationDescription<%= text(requirements.length > 0 ? "Status" : "") %> 
<%= h(requirement.getActor().getLabel()) %><%= h(siteLabel) %><%= requirement.getDescription() != null ? h(requirement.getDescription()) : HtmlString.NBSP %> + " style="font-weight:bold;"> + <%= text(requirement.isComplete() ? "Complete" : "Incomplete") %> + + + <%= link("Details", urlFor(ManageRequirementAction.class) + .addParameter("id", requirement.getRequestId()) + .addParameter("requirementId", requirement.getRowId()))%> +
+ + + + <%= button("Add Requirement").submit(true).onClick("if (validateNewRequirement()) document.addRequirementForm.submit(); return false;") %>
+ <% + } + %> +
+ No specimens are associated with this request.
+ <%=specimenSearchButton%> + <%=importVialIdsButton%> +
diff --git a/specimen/src/org/labkey/specimen/view/manageRequestInputs.jsp b/specimen/src/org/labkey/specimen/view/manageRequestInputs.jsp new file mode 100644 index 00000000..0ac74bd1 --- /dev/null +++ b/specimen/src/org/labkey/specimen/view/manageRequestInputs.jsp @@ -0,0 +1,201 @@ +<% +/* + * 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.specimen.SpecimenRequestManager.SpecimenRequestInput"%> +<%@ page import="org.labkey.api.study.StudyUrls"%> +<%@ page import="org.labkey.api.view.HttpView"%> +<%@ page import="org.labkey.api.view.JspView"%> +<%@ page import="org.labkey.specimen.actions.ManageRequestInputsBean"%> +<%@ page import="org.labkey.specimen.actions.SpecimenController.HandleUpdateRequestInputsAction" %> +<%@ taglib prefix="labkey" uri="http://www.labkey.org/taglib" %> +<%@ page extends="org.labkey.api.jsp.JspBase" %> +<% + JspView me = (JspView) HttpView.currentView(); + ManageRequestInputsBean bean = me.getModelBean(); + + String tdButtons = "\n" + + "\n" + + "\n"; + + String tdTitle = ""; + String tdHelpText = ""; + String tdMultiline = ""; + String tdRequired = ""; + String tdRememberSiteValue = ""; +%> + + + + + + + + + + + + <% + SpecimenRequestInput[] inputs = bean.getInputs(); + for (int inputIndex = 0; inputIndex < inputs.length; inputIndex++) + { + SpecimenRequestInput input = inputs[inputIndex]; + %> + + + + + + + + + <% + } + %> +
 TitleHelp TextMultilineRequiredRemember by Site<%= helpPopup("Remember by Location", + "If checked, the input will be pre-populated with the previous value entered for the destination location.")%>
<%= text(tdButtons) %>>>>
+ <%= button("Add New Input").submit(true).onClick("return addRow();") %> + <%= button("Save").submit(true) %> + <%= button("Cancel").href(urlProvider(StudyUrls.class).getManageStudyURL(bean.getContainer())) %> +
\ No newline at end of file diff --git a/specimen/src/org/labkey/specimen/view/manageRequestStatus.jsp b/specimen/src/org/labkey/specimen/view/manageRequestStatus.jsp new file mode 100644 index 00000000..4dacb718 --- /dev/null +++ b/specimen/src/org/labkey/specimen/view/manageRequestStatus.jsp @@ -0,0 +1,120 @@ +<% +/* + * 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.specimen.SpecimenRequestManager" %> +<%@ page import="org.labkey.api.specimen.SpecimenRequestStatus" %> +<%@ page import="org.labkey.api.study.SpecimenService"%> +<%@ page import="org.labkey.api.view.HttpView"%> +<%@ page import="org.labkey.api.view.JspView"%> +<%@ page import="org.labkey.specimen.actions.ManageRequestBean" %> +<%@ page import="org.labkey.specimen.actions.SpecimenController" %> +<%@ page import="org.labkey.specimen.notifications.ActorNotificationRecipientSet" %> +<%@ page import="java.util.List" %> +<%@ page extends="org.labkey.api.jsp.JspBase" %> +<%@ taglib prefix="labkey" uri="http://www.labkey.org/taglib" %> +<% + JspView me = (JspView) HttpView.currentView(); + ManageRequestBean bean = me.getModelBean(); + List statuses = SpecimenRequestManager.get().getRequestStatuses(getContainer(), getUser()); +%> + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Request Description + +
Current Status + <% if (SpecimenService.get().getRequestCustomizer().canChangeStatus(getUser())) { %> + <% + } + else + { + for (SpecimenRequestStatus status : statuses) + { + if(bean.getSpecimenRequest().getStatusId() == status.getRowId()) + { %> + <%= h(status.getLabel()) %><% + } + } + } %> +
Comments
Supporting
Documents
+
+
+
+
+ +
Notify + <% + boolean hasInactiveEmailAddress = false; + List possibleNotifications = bean.getPossibleNotifications(); + for (ActorNotificationRecipientSet possibleNotification : possibleNotifications) + { + boolean hasEmailAddresses = possibleNotification.getAllEmailAddresses().length > 0; + if (hasEmailAddresses) + hasInactiveEmailAddress |= possibleNotification.hasInactiveEmailAddress(); + %> + > + <%=possibleNotification.getHtmlDescriptionAndLink(hasEmailAddresses, getActionURL())%>
+ <% + } + if (hasInactiveEmailAddress) + { + %> + + Include inactive users
+ <% + } + %> +
  + <%= button("Save Changes and Send Notifications").submit(true) %>  + <%= button("Cancel").href(SpecimenController.getManageRequestURL(getContainer(), bean.getSpecimenRequest().getRowId(), null)) %> +
+
diff --git a/specimen/src/org/labkey/specimen/view/manageRequirement.jsp b/specimen/src/org/labkey/specimen/view/manageRequirement.jsp new file mode 100644 index 00000000..17584fb1 --- /dev/null +++ b/specimen/src/org/labkey/specimen/view/manageRequirement.jsp @@ -0,0 +1,200 @@ +<% +/* + * 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.specimen.requirements.SpecimenRequestRequirement"%> +<%@ page import="org.labkey.api.study.Location"%> +<%@ 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.specimen.actions.SpecimenController" %> +<%@ page import="org.labkey.specimen.actions.SpecimenController.DeleteRequirementAction" %> +<%@ page import="org.labkey.specimen.actions.SpecimenController.ManageRequirementAction" %> +<%@ page import="org.labkey.specimen.actions.SpecimenController.ManageRequirementBean" %> +<%@ page import="org.labkey.specimen.notifications.ActorNotificationRecipientSet" %> +<%@ page import="java.util.List" %> +<%@ page extends="org.labkey.api.jsp.JspBase" %> +<%@ taglib prefix="labkey" uri="http://www.labkey.org/taglib" %> +<%! + @Override + public void addClientDependencies(ClientDependencies dependencies) + { + // TODO: --Ext3-- This should be declared as part of the included views + dependencies.add("clientapi/ext3"); + } +%> +<% + JspView me = (JspView) HttpView.currentView(); + ManageRequirementBean bean = me.getModelBean(); + SpecimenRequestRequirement requirement = bean.getRequirement(); + Location location = requirement.getLocation(); + String locationLabel = location != null ? location.getDisplayName() : "N/A"; + + ActionURL deleteURL = urlFor(DeleteRequirementAction.class) + .addParameter("id", requirement.getRequestId()) + .addParameter("requirementId", requirement.getRowId()); +%> + + + + + +<% + if (bean.isRequestManager() && bean.isFinalState()) + { +%> + + + + + + +<% + } +%> + + + + + + + <% + if (bean.isRequestManager()) + { +%> + + + +<% + if (!bean.isFinalState()) + { + %> + + + + <% + } + } + %> + + + + + + +
<%= link("View Request").href(SpecimenController.getManageRequestURL(getContainer(), requirement.getRequestId(), null))%>
Requirement Notes
+ This request is in a final state; no changes are allowed.
+ To make changes, you must + change the request's status to a non-final state. +
Requirement Details
+ + + + + + + + + + + + + + <% + if (!bean.isRequestManager()) + { + %> + + + + + <% + } + %> +
Actor<%= h(requirement.getActor().getLabel()) %>
Location<%= h(locationLabel) %>
Description<%= text(requirement.getDescription()) %>
Status + " style="font-weight:bold;"> + <%= text(requirement.isComplete() ? "Complete" : "Incomplete") %> + +
+
Change Status
+ + + + + + + + + + + + + + + + + + + + + + + + +
  + > + Complete +
Comments + +
Attachments +
+
+ +
Notify + <% + boolean hasInactiveEmailAddress = false; + List possibleNotifications = bean.getPossibleNotifications(); + for (ActorNotificationRecipientSet possibleNotification : possibleNotifications) + { + boolean hasEmailAddresses = possibleNotification.getAllEmailAddresses().length > 0; + if (hasEmailAddresses) + hasInactiveEmailAddress |= possibleNotification.hasInactiveEmailAddress(); + %> + + <%=checked(hasEmailAddresses && bean.isDefaultNotification(possibleNotification)) %>> + <%=possibleNotification.getHtmlDescriptionAndLink(hasEmailAddresses, getActionURL())%>
+ <% + } + if (hasInactiveEmailAddress) + { + %> + + Include inactive users
+ <% + } + %> +
  + <%= button("Save Changes and Send Notifications").submit(true) %>  + <%= button("Delete Requirement").submit(true).onClick("this.form.action='" + h(deleteURL) + "'") %>  + <%= button("Cancel").href(SpecimenController.getManageRequestURL(getContainer(), requirement.getRequestId(), null)) %> +
+
+
History
<% me.include(bean.getHistoryView(), out); %>
diff --git a/specimen/src/org/labkey/specimen/view/manageSpecimenWebPart.jsp b/specimen/src/org/labkey/specimen/view/manageSpecimenWebPart.jsp index 95291f5c..d6b1aeaa 100644 --- a/specimen/src/org/labkey/specimen/view/manageSpecimenWebPart.jsp +++ b/specimen/src/org/labkey/specimen/view/manageSpecimenWebPart.jsp @@ -15,7 +15,7 @@ * limitations under the License. */ %> -<%@ page import="org.labkey.specimen.actions.SpecimenController2.SpecimenWebPartForm" %> +<%@ page import="org.labkey.specimen.actions.SpecimenController.SpecimenWebPartForm" %> <%@ page import="org.labkey.api.view.HttpView" %> <%@ page import="org.labkey.api.view.JspView" %> <%@ page import="org.labkey.api.view.template.ClientDependencies" %> @@ -211,7 +211,7 @@ var data = {grouping1 : [grouping1.items.items[0].getValue(), grouping1.items.items[1].getValue(), grouping1.items.items[2].getValue()], grouping2 : [grouping2.items.items[0].getValue(), grouping2.items.items[1].getValue(), grouping2.items.items[2].getValue()]}; Ext4.Ajax.request({ - url : (LABKEY.ActionURL.buildURL('specimen2', 'saveSpecimenWebPartSettings')), + url : (LABKEY.ActionURL.buildURL('specimen', 'saveSpecimenWebPartSettings')), method : 'POST', success: function(){ window.location = LABKEY.ActionURL.buildURL("study", 'manageStudy.view', null, null); diff --git a/specimen/src/org/labkey/specimen/view/manageSpecimens.jsp b/specimen/src/org/labkey/specimen/view/manageSpecimens.jsp new file mode 100644 index 00000000..9ab3633b --- /dev/null +++ b/specimen/src/org/labkey/specimen/view/manageSpecimens.jsp @@ -0,0 +1,208 @@ +<% +/* + * Copyright (c) 2008-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="com.google.common.collect.Iterables"%> +<%@ page import="org.labkey.api.data.Container" %> +<%@ page import="org.labkey.api.exp.property.Domain" %> +<%@ page import="org.labkey.api.security.User" %> +<%@ page import="org.labkey.api.security.permissions.AdminPermission" %> +<%@ page import="org.labkey.api.specimen.SpecimenRequestManager" %> +<%@ page import="org.labkey.api.specimen.model.SpecimenTablesProvider" %> +<%@ page import="org.labkey.api.specimen.requirements.SpecimenRequestRequirementProvider" %> +<%@ page import="org.labkey.api.specimen.security.permissions.ManageRequestSettingsPermission" %> +<%@ page import="org.labkey.api.specimen.settings.SettingsManager" %> +<%@ page import="org.labkey.api.study.SpecimenService" %> +<%@ page import="org.labkey.api.study.SpecimenTransform" %> +<%@ page import="org.labkey.api.study.Study" %> +<%@ page import="org.labkey.api.study.StudyService" %> +<%@ page import="org.labkey.api.view.ActionURL" %> +<%@ page import="org.labkey.specimen.actions.SpecimenController.ChooseImporterAction" %> +<%@ page import="org.labkey.specimen.actions.SpecimenController.ConfigureRequestabilityRulesAction" %> +<%@ page import="org.labkey.specimen.actions.SpecimenController.ManageActorsAction" %> +<%@ page import="org.labkey.specimen.actions.SpecimenController.ManageDefaultReqsAction" %> +<%@ page import="org.labkey.specimen.actions.SpecimenController.ManageDisplaySettingsAction" %> +<%@ page import="org.labkey.specimen.actions.SpecimenController.ManageLocationTypesAction" %> +<%@ page import="org.labkey.specimen.actions.SpecimenController.ManageNotificationsAction" %> +<%@ page import="org.labkey.specimen.actions.SpecimenController.ManageRepositorySettingsAction" %> +<%@ page import="org.labkey.specimen.actions.SpecimenController.ManageRequestInputsAction" %> +<%@ page import="org.labkey.specimen.actions.SpecimenController.ManageSpecimenCommentsAction" %> +<%@ page import="org.labkey.specimen.actions.SpecimenController.ManageStatusesAction" %> +<%@ page import="java.util.Collection" %> +<%@ page extends="org.labkey.api.jsp.JspBase" %> +<%@ taglib prefix="labkey" uri="http://www.labkey.org/taglib" %> +<% + Container c = getContainer(); + Study study = StudyService.get().getStudy(c); + User user = getUser(); + String subjectNounSingle = study.getSubjectNounSingular().toLowerCase(); + + if (c.hasPermission(user, AdminPermission.class)) + { + if (!study.hasSourceStudy() && !study.isSnapshotStudy()) + { + SpecimenTablesProvider stp = new SpecimenTablesProvider(c, user, null); + Domain domainEvent = stp.getDomain("specimenevent",false); + Domain domainVial = stp.getDomain("vial",false); + Domain domainSpecimen = stp.getDomain("specimen",false); + + ActionURL specimenEventUrl = null; + ActionURL vialUrl = null; + ActionURL specimenUrl = null; + + Collection specimenTransforms = SpecimenService.get().getSpecimenTransforms(getContainer()); + specimenTransforms.removeIf(transform -> null == transform.getManageAction(getContainer(), getUser())); + int numberOfTransforms = specimenTransforms.size(); + + if (domainEvent != null) + { + specimenEventUrl = domainEvent.getDomainKind().urlEditDefinition(domainEvent, getViewContext()) + .addReturnURL(getViewContext().getActionURL()); + } + + if (domainVial != null) + { + vialUrl = domainVial.getDomainKind().urlEditDefinition(domainVial, getViewContext()) + .addReturnURL(getViewContext().getActionURL()); + } + + if (domainSpecimen != null) + { + specimenUrl = domainSpecimen.getDomainKind().urlEditDefinition(domainSpecimen, getViewContext()) + .addReturnURL(getViewContext().getActionURL()); + } + +%> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <% if (numberOfTransforms == 1) { %> + <% + SpecimenTransform transform = Iterables.get(specimenTransforms, 0); + ActionURL manageAction = transform.getManageAction(getContainer(), getUser()); + %> + + + + + + <% } else { %> + + + + + + <% + } + %> +
Repository TypeThis study uses the <%=text(SettingsManager.get().getRepositorySettings(study.getContainer()).isSimple() ? "standard" : "advanced")%> specimen repository<%=link("Change Repository Type", ManageRepositorySettingsAction.class)%>
Specimen FieldsCustomize specimen fields for this repository<%=link("Edit Specimen fields", specimenUrl)%>
Vial FieldsCustomize vial fields for this repository<%=link("Edit Vial fields", vialUrl)%>
Specimen Event FieldsCustomize specimen event fields for this repository<%=link("Edit Specimen Event fields", specimenEventUrl)%>
Display and BehaviorManage warnings, comments, and workflow<%= link("Manage Display and Behavior", ManageDisplaySettingsAction.class) %>
CommentsManage <%=h(subjectNounSingle)%> and <%=h(subjectNounSingle)%>/visit comments<%= link("Manage Comments", ManageSpecimenCommentsAction.class) %>
Specimen Web PartConfigure the specimen groupings in the specimen web part<%= link("Configure Specimen Groupings", SpecimenService.get().getManageSpecimenWebPartActionClass()) %>
Specimen ImportConfigure a specimen import<%=link("Configure specimen import", manageAction)%>
Specimen ImportChoose and configure a specimen import<%=link("Configure specimen import", ChooseImporterAction.class)%>
+
+ <% + } + else + { + String childStudyType = study.getStudySnapshotType().getTitle().toLowerCase(); + %> +

Note: specimen repository and request settings are not available for <%=text(childStudyType)%> studies.

+ <% + } + } // admin permission + + if (c.hasPermission(user, ManageRequestSettingsPermission.class)) + { + if (SettingsManager.get().getRepositorySettings(getContainer()).isEnableRequests()) + { + %> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Location TypesConfigure which location types are allowed to be requesting locations<%= link("Manage Location Types", ManageLocationTypesAction.class) %>
StatusesThis study defines <%=SpecimenRequestManager.get().getRequestStatuses(study.getContainer(), getUser()).size() %> specimen request + statuses<%= link("Manage Request Statuses", ManageStatusesAction.class) %>
ActorsThis study defines <%=SpecimenRequestRequirementProvider.get().getActors(getContainer()).length %> specimen request + actors<%= link("Manage Actors and Groups", ManageActorsAction.class) %>
Request RequirementsManage default requirements for new requests<%= link("Manage Default Requirements", ManageDefaultReqsAction.class) %>
Request FormManage inputs required for a new specimen request <%= link("Manage New Request Form", ManageRequestInputsAction.class) %>
NotificationsManage specimen request notifications<%= link("Manage Notifications", ManageNotificationsAction.class) %>
Requestability RulesManage the rules used to determine specimen availability for request<%= link("Manage Requestability Rules", ConfigureRequestabilityRulesAction.class) %>
+
+<% + } + } +%> diff --git a/specimen/src/org/labkey/specimen/view/manageStatusOrder.jsp b/specimen/src/org/labkey/specimen/view/manageStatusOrder.jsp new file mode 100644 index 00000000..6b409315 --- /dev/null +++ b/specimen/src/org/labkey/specimen/view/manageStatusOrder.jsp @@ -0,0 +1,115 @@ +<% +/* + * 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.specimen.SpecimenRequestManager"%> +<%@ page import="org.labkey.api.specimen.SpecimenRequestStatus"%> +<%@ page import="org.labkey.api.study.Study"%> +<%@ page import="org.labkey.api.view.HttpView"%> +<%@ page import="org.labkey.api.view.JspView"%> +<%@ page import="java.util.List" %> +<%@ page import="org.labkey.specimen.actions.SpecimenController" %> +<%@ page import="org.labkey.specimen.actions.SpecimenController.ManageStatusOrderAction" %> +<%@ page import="org.labkey.specimen.actions.SpecimenController.ManageStatusesAction" %> +<%@ taglib prefix="labkey" uri="http://www.labkey.org/taglib" %> +<%@ page extends="org.labkey.api.jsp.JspBase" %> +<% + JspView me = (JspView) HttpView.currentView(); + Study study = me.getModelBean(); + List statuses = SpecimenRequestManager.get().getRequestStatuses(study.getContainer(), getUser()); +%> + + + + + + + + + +
+ <% + %> + + + <%= button("Move Up").submit(true).onClick("return orderModule(0)") %>
+ <%= button("Move Down").submit(true).onClick("return orderModule(1)") %> +
+
+ + <%= button("Save").submit(true) %> <%= button("Cancel").href(urlFor(ManageStatusesAction.class)) %> +
diff --git a/specimen/src/org/labkey/specimen/view/manageStatuses.jsp b/specimen/src/org/labkey/specimen/view/manageStatuses.jsp new file mode 100644 index 00000000..98611899 --- /dev/null +++ b/specimen/src/org/labkey/specimen/view/manageStatuses.jsp @@ -0,0 +1,146 @@ +<% +/* + * 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.specimen.SpecimenRequestManager"%> +<%@ page import="org.labkey.api.specimen.SpecimenRequestStatus"%> +<%@ page import="org.labkey.api.specimen.settings.SettingsManager"%> +<%@ page import="org.labkey.api.specimen.settings.StatusSettings"%> +<%@ page import="org.labkey.api.study.Study"%> +<%@ 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="java.util.List" %> +<%@ page import="java.util.Set" %> +<%@ page import="org.labkey.specimen.actions.SpecimenController" %> +<%@ page import="org.labkey.specimen.actions.SpecimenController.DeleteStatusAction" %> +<%@ page import="org.labkey.specimen.actions.SpecimenController.ManageStatusesAction" %> +<%@ page import="org.labkey.specimen.actions.SpecimenController.ManageStatusOrderAction" %> +<%@ taglib prefix="labkey" uri="http://www.labkey.org/taglib" %> +<%@ page extends="org.labkey.api.jsp.JspBase" %> +<% + JspView me = (JspView) HttpView.currentView(); + Study study = me.getModelBean(); + List statuses = SpecimenRequestManager.get().getRequestStatuses(study.getContainer(), getUser()); + Set inUseStatuses = SpecimenRequestManager.get().getRequestStatusIdsInUse(study.getContainer()); + StatusSettings settings = SettingsManager.get().getStatusSettings(study.getContainer()); + boolean showSystemStatuses = settings.isUseShoppingCart(); +%> + + + +

+ Request statuses help a coordinator organize and track requests through the system and + communicate progress to requesters. All requests do not need to pass through all states. +

+ + + + + + + + + + <% + if (statuses != null && statuses.size() > 0) + { + for(SpecimenRequestStatus status : statuses) + { + %> + <%= text(!showSystemStatuses && status.isSystemStatus() ? "style=\"display:none\"" : "") %>> + + + + + <% + if (status.isSystemStatus() || inUseStatuses.contains(status.getRowId())) + { + %> + + <% + } + else + { + %> + + <% + } + %> + + <% + } + } + %> + + + + + + + + + + +
<%= text(statuses != null && statuses.size()> 0 ? "Step Number" : " ") %>Status NameFinal State<%= helpPopup("Final States", "States are final if they indicate no further processing will occur for a request. For example, 'Completed', or 'Rejected' could be final states.")%>Lock Specimens<%= helpPopup("Locked Specimen States", "Specifies whether specimens should be available for additional requests while in each status.")%> 
<%= status.isSystemStatus() ? 1 : status.getSortOrder() + 1 %> + <% + if (!status.isSystemStatus()) + { + %> + + <% + } + %> + " + <%=disabled(status.isSystemStatus()) %>> + <%=disabled(status.isSystemStatus()) %>> + + <%=disabled(status.isSystemStatus()) %>> + In-use<%= helpPopup("In-use Status", status.isSystemStatus() ? "This is a system status that cannot be deleted." : + "This status cannot be deleted because one or more requests are currently in this status.") %><%=link("delete").href(urlFor(DeleteStatusAction.class).addParameter("id", status.getRowId())).usePost()%>
New Status: 
+
+ <%= button("Save").submit(true) %>  + <%= button("Change Order").submit(true).onClick("document.manageStatuses.nextPage.value='" + urlFor(ManageStatusOrderAction.class) + "'; return true;") %> + +
+ +
+ +

+ Allowing users to build up specimen requests over multiple + searches is generally more convenient, but requires the coordinator to watch for abandoned unsubmitted requests. +

+
+ +
+ + <%= button("Done").submit(true).onClick("document.manageStatuses.nextPage.value=''; return true;")%> + <%= button("Cancel").href(urlProvider(StudyUrls.class).getManageStudyURL(study.getContainer())) %>  +
\ No newline at end of file diff --git a/specimen/src/org/labkey/specimen/view/requestSpecimens.jsp b/specimen/src/org/labkey/specimen/view/requestSpecimens.jsp index 0efb413a..8a4f624f 100644 --- a/specimen/src/org/labkey/specimen/view/requestSpecimens.jsp +++ b/specimen/src/org/labkey/specimen/view/requestSpecimens.jsp @@ -17,20 +17,21 @@ %> <%@ page import="org.labkey.api.data.Container"%> <%@ page import="org.labkey.api.specimen.SpecimenManager"%> -<%@ page import="org.labkey.api.specimen.SpecimenMigrationService"%> <%@ page import="org.labkey.api.specimen.SpecimenRequestManager.SpecimenRequestInput"%> <%@ page import="org.labkey.api.specimen.Vial"%> <%@ page import="org.labkey.api.specimen.location.LocationImpl"%> <%@ page import="org.labkey.api.specimen.location.LocationManager"%> <%@ page import="org.labkey.api.specimen.settings.SettingsManager"%> <%@ page import="org.labkey.api.study.SpecimenService"%> +<%@ page import="org.labkey.api.view.ActionURL"%> <%@ page import="org.labkey.api.view.HttpView" %> <%@ page import="org.labkey.api.view.JspView" %> <%@ page import="org.labkey.api.view.ViewContext" %> <%@ page import="org.labkey.api.view.template.ClientDependencies" %> -<%@ page import="org.labkey.specimen.actions.SpecimenController2.CreateSpecimenRequestForm" %> -<%@ page import="org.labkey.specimen.actions.SpecimenController2.HandleCreateSpecimenRequestAction" %> -<%@ page import="org.labkey.specimen.actions.SpecimenController2.NewRequestBean" %> +<%@ page import="org.labkey.specimen.actions.SpecimenController.CreateSpecimenRequestForm" %> +<%@ page import="org.labkey.specimen.actions.SpecimenController.HandleCreateSpecimenRequestAction" %> +<%@ page import="org.labkey.specimen.actions.SpecimenController.NewRequestBean" %> +<%@ page import="org.labkey.specimen.actions.SpecimenController.ViewRequestsAction" %> <%@ page import="org.springframework.validation.BindException" %> <%@ page import="org.springframework.validation.ObjectError" %> <%@ page import="java.util.List" %> @@ -221,7 +222,7 @@ function setDefaults() <% } %> - <%= text(hasReturnURL ? button("Cancel").href(bean.getReturnUrl()).toString() : button("Cancel").href(SpecimenMigrationService.get().getViewRequestsURL(getContainer())).toString()) %> + <%= text(hasReturnURL ? button("Cancel").href(bean.getReturnUrl()).toString() : button("Cancel").href(new ActionURL(ViewRequestsAction.class, getContainer())).toString()) %> diff --git a/specimen/src/org/labkey/specimen/view/selectSpecimenProvider.jsp b/specimen/src/org/labkey/specimen/view/selectSpecimenProvider.jsp index e53fedf4..28d6160f 100644 --- a/specimen/src/org/labkey/specimen/view/selectSpecimenProvider.jsp +++ b/specimen/src/org/labkey/specimen/view/selectSpecimenProvider.jsp @@ -15,10 +15,10 @@ * limitations under the License. */ %> -<%@ page import="org.labkey.api.specimen.actions.SelectSpecimenProviderBean"%> -<%@ page import="org.labkey.api.specimen.location.LocationImpl" %> +<%@ page import="org.labkey.api.specimen.location.LocationImpl"%> <%@ page import="org.labkey.api.view.HttpView" %> <%@ page import="org.labkey.api.view.JspView" %> +<%@ page import="org.labkey.specimen.actions.SelectSpecimenProviderBean" %> <%@ taglib prefix="labkey" uri="http://www.labkey.org/taglib" %> <%@ page extends="org.labkey.api.jsp.JspBase" %> <% diff --git a/specimen/src/org/labkey/specimen/view/specimen.jsp b/specimen/src/org/labkey/specimen/view/specimen.jsp new file mode 100644 index 00000000..72e17406 --- /dev/null +++ b/specimen/src/org/labkey/specimen/view/specimen.jsp @@ -0,0 +1,106 @@ +<% +/* + * 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.specimen.SpecimenManager"%> +<%@ page import="org.labkey.api.specimen.Vial"%> +<%@ page import="org.labkey.api.specimen.location.LocationManager"%> +<%@ page import="org.labkey.api.specimen.model.SpecimenComment" %> +<%@ page import="org.labkey.api.study.Location" %> +<%@ page import="org.labkey.api.study.StudyService" %> +<%@ 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.specimen.actions.SpecimenController" %> +<%@ page import="org.labkey.specimen.actions.SpecimenEventBean" %> +<%@ page import="org.labkey.specimen.security.permissions.SetSpecimenCommentsPermission" %> +<%@ page extends="org.labkey.api.jsp.JspBase" %> +<% + JspView me = (JspView) HttpView.currentView(); + SpecimenEventBean bean = me.getModelBean(); + Vial vial = bean.getVial(); + Location originatingLocation = LocationManager.get().getOriginatingLocation(vial); + SpecimenComment comment = SpecimenManager.get().getSpecimenCommentForVial(vial); + ActionURL commentsLink = new ActionURL(SpecimenController.UpdateCommentsAction.class, vial.getContainer()); + commentsLink.addParameter("rowId", vial.getRowId()); + commentsLink.addParameter("referrer", getActionURL().getLocalURIString()); +%> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Globally Unique ID<%= h(vial.getGlobalUniqueId()) %>
<%= h(StudyService.get().getSubjectNounSingular(vial.getContainer())) %><%= h(id(vial.getPtid())) %>
Visit<%= h(vial.getVisitDescription()) %> <%= vial.getVisitValue() %>
Volume<%= h(vial.getVolume()) %> <%= h(vial.getVolumeUnits()) %>
Collection Date<%=vial.getDrawTimestamp() != null ? formatDateTime(vial.getDrawTimestamp()) : h("Unknown")%>
Collection Location<%= h(originatingLocation != null ? originatingLocation.getDisplayName() : "Unknown") %>
Comments and QC + <%= h(comment != null ? comment.getComment() : null) %> + <% if (vial.getContainer().hasPermission(getUser(), SetSpecimenCommentsPermission.class)) { %> + <%= link("update", commentsLink) %> + <% } %> +
+
+<% +if (comment != null) +{ + if (comment.isQualityControlFlag()) + { +%> + Vial is flagged for quality control. +<% + } + if (comment.getQualityControlComments() != null && comment.getQualityControlComments().length() > 0) + { +%> + <%= h(comment.getQualityControlComments()) %>
+<% + } + if (comment.isQualityControlFlagForced()) + { +%> +
NOTE: Quality control state for this vial was set manually; it will remain + <%= text(comment.isQualityControlFlag() ? "flagged" : "unflagged") %> until manually changed.
+<% + } +} +if (bean.getReturnUrl() != null && bean.getReturnUrl().length() > 0) +{ +%> +
<%=link("return to vial view").href(bean.getReturnUrl() )%> +<% + } +%> diff --git a/specimen/src/org/labkey/specimen/view/specimenHeader.jsp b/specimen/src/org/labkey/specimen/view/specimenHeader.jsp new file mode 100644 index 00000000..75e7336f --- /dev/null +++ b/specimen/src/org/labkey/specimen/view/specimenHeader.jsp @@ -0,0 +1,144 @@ +<% +/* + * 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.security.permissions.AdminPermission"%> +<%@ page import="org.labkey.api.study.StudyService"%> +<%@ page import="org.labkey.api.study.StudyUrls"%> +<%@ page import="org.labkey.api.util.Pair"%> +<%@ 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.specimen.actions.ShowSearchAction" %> +<%@ page import="org.labkey.specimen.actions.SpecimenController" %> +<%@ page import="org.labkey.specimen.actions.SpecimenController.SpecimensAction" %> +<%@ page import="org.labkey.specimen.actions.SpecimenHeaderBean" %> +<%@ page import="java.util.Iterator" %> +<%@ page extends="org.labkey.api.jsp.JspBase" %> +<%! + @Override + public void addClientDependencies(ClientDependencies dependencies) + { + dependencies.add("clientapi/ext3"); + dependencies.add("study/sampleRequest.js"); + } +%> +<% + JspView me = (JspView) HttpView.currentView(); + SpecimenHeaderBean bean = me.getModelBean(); + ActionURL createRequestURL = new ActionURL(ShowSearchAction.class, getContainer()); + createRequestURL.addParameter("fromGroupedView", !bean.isShowingVials()); + createRequestURL.addReturnURL(getActionURL()); + String subjectNounSingle = StudyService.get().getSubjectNounSingular(getContainer()); + String subjectNounPlural = StudyService.get().getSubjectNounPlural(getContainer()); +%> + +<% + // boolean enableRequests = SampleManager.getInstance().getRepositorySettings(getContainer()).isEnableRequests(); + String vialLinkText = bean.isShowingVials() ? "Group vials" : "Show individual vials"; + + if (getContainer().hasPermission(getUser(), AdminPermission.class)) + { +%> +<%=link("Manage Study", urlProvider(StudyUrls.class).getManageStudyClass())%>  +<% + } +%> +<%=link(vialLinkText, bean.getOtherViewURL())%>  +<%=link("Search").href(ShowSearchAction.getShowSearchURL(getContainer(), bean.isShowingVials()))%>  +<%=link("Reports", urlFor(SpecimenController.AutoReportListAction.class)) %> +<% + if (!bean.getFilteredPtidVisits().isEmpty()) + { + // get the first visit label: + StringBuilder filterString = new StringBuilder(); + filterString.append("This view is displaying specimens only from "); + boolean usePlural = bean.getFilteredPtidVisits().size() != 1; + if (bean.isSingleVisitFilter()) + { + filterString.append(h((usePlural?subjectNounPlural:subjectNounSingle).toLowerCase())).append(" "); + for (Iterator> it = bean.getFilteredPtidVisits().iterator(); it.hasNext();) + { + String ptid = it.next().getKey(); + filterString.append(ptid); + if (it.hasNext()) + filterString.append(", "); + } + String visit = bean.getFilteredPtidVisits().iterator().next().getValue(); + if (visit != null) + filterString.append(" at visit ").append(visit); + filterString.append(".
"); + } + else + { + filterString.append(" the following ").append(h(subjectNounSingle.toLowerCase())).append("/visit ").append(usePlural?"pairs":"pair").append(":
"); + for (Iterator> it = bean.getFilteredPtidVisits().iterator(); it.hasNext();) + { + Pair ptidVisit = it.next(); + filterString.append(ptidVisit.getKey()).append("/").append(ptidVisit.getValue()); + if (it.hasNext()) + filterString.append(", "); + } + filterString.append("."); + } + ActionURL noFitlerUrl = getViewContext().cloneActionURL().setAction(SpecimensAction.class); +%> +

+ + +
<%= unsafe(filterString.toString()) %>
+

+<%= link("Remove " + subjectNounSingle + "/Visit Filter", noFitlerUrl)%><% + } +%> +
+ + + + + + + + + + +
+ + + + + + +
Select request:<%=link("Create new request").href("#").onClick("createRequest(); return false;").id("sample-request-create-link")%>
+
+ +
+ +
+
diff --git a/specimen/src/org/labkey/specimen/view/updateComments.jsp b/specimen/src/org/labkey/specimen/view/updateComments.jsp new file mode 100644 index 00000000..16e23a19 --- /dev/null +++ b/specimen/src/org/labkey/specimen/view/updateComments.jsp @@ -0,0 +1,258 @@ +<% +/* + * 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.apache.commons.lang3.StringUtils" %> +<%@ page import="org.labkey.api.data.Container" %> +<%@ page import="org.labkey.api.security.User" %> +<%@ page import="org.labkey.api.specimen.Vial" %> +<%@ page import="org.labkey.api.specimen.settings.SettingsManager" %> +<%@ page import="org.labkey.api.study.Dataset" %> +<%@ page import="org.labkey.api.study.Study" %> +<%@ page import="org.labkey.api.study.StudyInternalService" %> +<%@ page import="org.labkey.api.study.StudyService" %> +<%@ 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.PopupMenu" %> +<%@ page import="org.labkey.api.view.template.ClientDependencies" %> +<%@ page import="org.labkey.api.view.template.FrameFactoryClassic" %> +<%@ page import="org.labkey.specimen.actions.SpecimenController.UpdateCommentsAction" %> +<%@ page import="org.labkey.specimen.actions.UpdateSpecimenCommentsBean" %> +<%@ page import="java.util.Map" %> +<%@ taglib prefix="labkey" uri="http://www.labkey.org/taglib" %> +<%@ page extends="org.labkey.api.jsp.JspBase" %> +<%! + @Override + public void addClientDependencies(ClientDependencies dependencies) + { + // TODO: --Ext3-- This should be declared as part of the included views + dependencies.add("clientapi/ext3"); + } +%> +<% + JspView me = (JspView) HttpView.currentView(); + UpdateSpecimenCommentsBean bean = me.getModelBean(); + Container container = getContainer(); + + NavTree copyButton = createCopyCommentButton(bean.getParticipantVisitMap(), StudyService.get().getStudy(container), getUser()); +%> + + + + + +<% + if (SettingsManager.get().getDisplaySettings(container).isEnableManualQCFlagging()) + { + FrameFactoryClassic.startTitleFrame(out, "Quality Control Flags", null, null, null); + if (bean.isMixedFlagState()) + { +%> + Note: A subset of the selected vials have quality control flags. See vial list below for details.

+<% + } +%> + What quality control state should be applied to the selected vials?
+ Do not change quality control state
+<% + // allow users to flag if our current state is mixed or unflagged: + if (bean.isMixedFlagState() || !bean.isCurrentFlagState()) + { +%> + Add quality control flag
+<% + } + // allow users to unflag if our current state is mixed or flagged: + if (bean.isMixedFlagState() || bean.isCurrentFlagState()) + { +%> + Remove quality control flag

+<% + } + FrameFactoryClassic.endTitleFrame(out); + } + FrameFactoryClassic.startTitleFrame(out, "Comments", null, null, null); +%> + + <% + if (bean.isMixedComments()) + { + %> + Note: Some or all of the selected vials have existing comments. See vial list below for details.

+ What action should be performed on vials with existing comments?
+ Replace existing comments with new comments
+ Append new comments to existing comments
+ Do not change comments for vials with existing comments

+ New comment:
+ <% + } + else + { + %> + + <% + } + %> + + + <% + for (Vial vial : bean.getVials()) + { + %> + + <% + } + %> + + + + + + + +
+
+ +
+ <%= button("Save Changes").submit(true) %> + <% + if (!StringUtils.isBlank(bean.getCurrentComment()) && copyButton != null) + { + PopupMenu menu = new PopupMenu(copyButton); + menu.render(out); + } + %> + <%= button("Cancel").href(new ActionURL(bean.getReferrer())) %> +
+ +<% + FrameFactoryClassic.endTitleFrame(out); + FrameFactoryClassic.startTitleFrame(out, "Selected Vials", null, null, null); +%> +<% me.include(bean.getSpecimenQueryView(), out); %> +<% + FrameFactoryClassic.endTitleFrame(out); +%> + +<%! + private NavTree createCopyCommentButton(Map> pvMap, Study study, User user) + { + Integer participantCommentDatasetId = StudyInternalService.get().getParticipantCommentDatasetId(study); + Integer participantVisitCommentDatasetId = StudyInternalService.get().getParticipantVisitCommentDatasetId(study); + boolean hasParticipantMenu = participantCommentDatasetId != null && participantCommentDatasetId != -1; + boolean hasParticipantVisitMenu = participantVisitCommentDatasetId != null && participantVisitCommentDatasetId != -1; + + if (hasParticipantMenu) + { + Dataset ds = StudyService.get().getDataset(study.getContainer(), participantCommentDatasetId); + hasParticipantMenu = ds != null && ds.canUpdate(user); + } + + if (hasParticipantVisitMenu) + { + Dataset ds = StudyService.get().getDataset(study.getContainer(), participantVisitCommentDatasetId); + hasParticipantVisitMenu = ds != null && ds.canUpdate(user); + } + + if (hasParticipantMenu || hasParticipantVisitMenu) + { + NavTree button = new NavTree("Copy or Move Comment(s)"); + + NavTree moveButton = new NavTree("Move"); + moveButton.setId("Comment:Move"); + NavTree copyButton = new NavTree("Copy"); + copyButton.setId("Comment:Copy"); + + button.addChild(moveButton); + button.addChild(copyButton); + + addParticipantMenuItems(moveButton, pvMap, hasParticipantMenu, hasParticipantVisitMenu, true); + addParticipantMenuItems(copyButton, pvMap, hasParticipantMenu, hasParticipantVisitMenu, false); + + return button; + } + return null; + } + + private void addParticipantMenuItems(NavTree button, Map> pvMap, + boolean hasParticipantMenu, boolean hasParticipantVisitMenu, boolean isMove) + { + String subjectNoun = StudyService.get().getSubjectNounSingular(getContainer()); + // participant comments + if (hasParticipantMenu) + { + StringBuilder sb = new StringBuilder(); + NavTree participantItem = new NavTree("To " + subjectNoun, "#"); + participantItem.setId(isMove ? "Move:ToParticipant" : "Copy:ToParticipant"); + button.addChild(participantItem); + + for (String ptid : pvMap.keySet()) + { + NavTree subItem = new NavTree(ptid, "#"); + sb.setLength(0); + if (isMove) + sb.append("if (confirm('This will permanently remove all vial comments for the displayed vials. Continue?')){"); + sb.append("document.updateComments.copyToParticipant.value='true';"); + sb.append("document.updateComments.copyParticipantId.value='").append(ptid).append("';"); + sb.append("document.updateComments.deleteVialComment.value='").append(isMove).append("';"); + sb.append("document.updateComments.submit()"); + if (isMove) + sb.append("}"); + + subItem.setScript(sb.toString()); + if (isMove) + subItem.setId("MovePtid:" + ptid); + else + subItem.setId("CopyPtid:" + ptid); + participantItem.addChild(subItem); + } + } + + // participant/visit comments + if (hasParticipantVisitMenu) + { + StringBuilder sb = new StringBuilder(); + NavTree participantVisitItem = new NavTree("To " + subjectNoun + "/Visit", "#"); + participantVisitItem.setId(isMove ? "Move:ToParticipantVisit" : "Copy:ToParticipantVisit"); + button.addChild(participantVisitItem); + for (Map.Entry> entry : pvMap.entrySet()) + { + NavTree ptidItem = new NavTree(entry.getKey()); + ptidItem.setId("PtidVisit:" + entry.getKey()); + + for (Map.Entry visitEntry : entry.getValue().entrySet()) + { + NavTree visitItem = new NavTree(visitEntry.getKey()); + sb.setLength(0); + if (isMove) + sb.append("if (confirm('This will permanently remove all vial comments for the displayed vials. Continue?')){"); + sb.append("document.updateComments.copyToParticipant.value='true';"); + sb.append("document.updateComments.copySampleId.value='").append(visitEntry.getValue()).append("';"); + sb.append("document.updateComments.deleteVialComment.value='").append(isMove).append("';"); + sb.append("document.updateComments.submit()"); + if (isMove) + sb.append("}"); + + visitItem.setScript(sb.toString()); + ptidItem.addChild(visitItem); + } + participantVisitItem.addChild(ptidItem); + } + } + } +%> diff --git a/specimen/src/org/labkey/specimen/view/uploadSimpleSpecimens.jsp b/specimen/src/org/labkey/specimen/view/uploadSimpleSpecimens.jsp index 19780236..e225c68d 100644 --- a/specimen/src/org/labkey/specimen/view/uploadSimpleSpecimens.jsp +++ b/specimen/src/org/labkey/specimen/view/uploadSimpleSpecimens.jsp @@ -22,7 +22,7 @@ <%@ page import="org.labkey.api.view.HttpView" %> <%@ page import="org.labkey.api.view.JspView" %> <%@ page import="org.labkey.specimen.actions.ShowUploadSpecimensAction" %> -<%@ page import="org.labkey.specimen.actions.SpecimenController2.GetSpecimenExcelAction" %> +<%@ page import="org.labkey.specimen.actions.SpecimenController.GetSpecimenExcelAction" %> <%@ page extends="org.labkey.api.jsp.JspBase" %> <%@ taglib prefix="labkey" uri="http://www.labkey.org/taglib" %> <% diff --git a/specimen/src/org/labkey/specimen/view/webPart.jsp b/specimen/src/org/labkey/specimen/view/webPart.jsp new file mode 100644 index 00000000..b88638b2 --- /dev/null +++ b/specimen/src/org/labkey/specimen/view/webPart.jsp @@ -0,0 +1,366 @@ +<% +/* + * 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.security.User"%> +<%@ page import="org.labkey.api.security.permissions.AdminPermission"%> +<%@ page import="org.labkey.api.specimen.security.permissions.RequestSpecimensPermission"%> +<%@ page import="org.labkey.api.specimen.settings.SettingsManager" %> +<%@ page import="org.labkey.specimen.view.SpecimenWebPart" %> +<%@ 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.specimen.actions.ShowSearchAction" %> +<%@ page import="org.labkey.specimen.actions.ShowUploadSpecimensAction" %> +<%@ page import="org.labkey.specimen.actions.SpecimenController" %> +<%@ page import="org.labkey.specimen.actions.SpecimenController.AutoReportListAction" %> +<%@ page import="org.labkey.specimen.actions.SpecimenController.ShowCreateSpecimenRequestAction" %> +<%@ page import="org.labkey.specimen.actions.SpecimenController.ViewRequestsAction" %> +<%@ page extends="org.labkey.api.jsp.JspBase" %> +<% + SpecimenWebPart.SpecimenWebPartBean bean = (SpecimenWebPart.SpecimenWebPartBean) HttpView.currentView().getModelBean(); + + Container c = getContainer(); + User user = getUser(); + String time = Long.toString(System.currentTimeMillis()); + boolean isAdmin = c.hasPermission(user, AdminPermission.class); + + String contentSpanName = "specimen-browse-webpart-content" + time; + String groupHeading1 = "groupHeading1-" + time; + String groupHeading2 = "groupHeading2-" + time; + String group1 = "group1-" + time; + String group2 = "group2-" + time; + String groupControl1 = "groupControl1-" + time; + String groupControl2 = "groupControl2-" + time; +%> + +

From 473a912afac784072cd56fe9154810ac0bf8fe40 Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Tue, 15 Jun 2021 12:54:11 -0700 Subject: [PATCH 339/494] Rename primary specimen controller Original PR: https://github.com/LabKey/testAutomation/pull/762 --- src/org/labkey/test/tests/SpecimenExportTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/org/labkey/test/tests/SpecimenExportTest.java b/src/org/labkey/test/tests/SpecimenExportTest.java index bd25b311..fee4bfba 100644 --- a/src/org/labkey/test/tests/SpecimenExportTest.java +++ b/src/org/labkey/test/tests/SpecimenExportTest.java @@ -141,7 +141,7 @@ private void verifySpecimenSettingsInArchive() File studyXml = new File(TestFileUtils.getDefaultFileRoot(getProjectName() + "/" + getFolderName()), "export/study/study.xml"); waitFor(studyXml::exists, "Couldn't find exported study: " + studyXml, WAIT_FOR_JAVASCRIPT); String studyXmlText = TestFileUtils.getFileContents(studyXml).replaceAll(" *\r*\n *", "\n").replaceAll(" +", " "); - assertTrue(studyXmlText.contains("")); + assertTrue("study.xml did not include expected element: " + studyXmlText, studyXmlText.contains("")); log("verify specimen_settings.xml"); File specimenSettingsXml = new File(TestFileUtils.getDefaultFileRoot(getProjectName() + "/" + getFolderName()), "export/study/specimens/specimen_settings.xml"); From 785f124fbbcdfc592834dcbe5a32774f7bcb5073 Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Mon, 21 Jun 2021 11:35:46 -0700 Subject: [PATCH 340/494] Fix specimen navtrail NPE Original PR: https://github.com/LabKey/platform/pull/2373 --- .../src/org/labkey/specimen/actions/SpecimenController.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/specimen/src/org/labkey/specimen/actions/SpecimenController.java b/specimen/src/org/labkey/specimen/actions/SpecimenController.java index 6e645c61..0be02ff4 100644 --- a/specimen/src/org/labkey/specimen/actions/SpecimenController.java +++ b/specimen/src/org/labkey/specimen/actions/SpecimenController.java @@ -26,6 +26,7 @@ import org.labkey.api.data.*; import org.labkey.api.gwt.client.AuditBehaviorType; import org.labkey.api.module.FolderType; +import org.labkey.api.module.Module; import org.labkey.api.pipeline.PipeRoot; import org.labkey.api.pipeline.PipelineService; import org.labkey.api.pipeline.PipelineStatusUrls; @@ -77,7 +78,6 @@ import org.labkey.api.specimen.security.permissions.ManageRequestSettingsPermission; import org.labkey.api.specimen.security.permissions.ManageRequestsPermission; import org.labkey.api.specimen.security.permissions.RequestSpecimensPermission; -import org.labkey.specimen.security.permissions.SetSpecimenCommentsPermission; import org.labkey.api.specimen.settings.DisplaySettings; import org.labkey.api.specimen.settings.RepositorySettings; import org.labkey.api.specimen.settings.RequestNotificationSettings; @@ -139,6 +139,7 @@ import org.labkey.specimen.security.permissions.ManageRequestRequirementsPermission; import org.labkey.specimen.security.permissions.ManageRequestStatusesPermission; import org.labkey.specimen.security.permissions.ManageSpecimenActorsPermission; +import org.labkey.specimen.security.permissions.SetSpecimenCommentsPermission; import org.labkey.specimen.view.SpecimenSearchWebPart; import org.labkey.specimen.view.SpecimenWebPart; import org.springframework.validation.BindException; @@ -249,7 +250,8 @@ public void addRootNavTrail(NavTree root) Container c = getContainer(); ActionURL rootURL; FolderType folderType = c.getFolderType(); - if ("study".equals(folderType.getDefaultModule().getName())) + Module module = folderType.getDefaultModule(); + if (null != module && "study".equals(folderType.getDefaultModule().getName())) { rootURL = folderType.getStartURL(c, getUser()); } From 6dbd918e87ceaaa6006a9be668bb9da4e0f3adb8 Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Mon, 21 Jun 2021 11:36:38 -0700 Subject: [PATCH 341/494] CachedResultSet: implement getBigDecimal() methods Original PR: https://github.com/LabKey/platform/pull/2372 Convert more Double sequence number references to BigDecimal --- .../specimen/actions/SpecimenController.java | 2 +- .../actions/UpdateSpecimenCommentsBean.java | 14 ++------------ 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/specimen/src/org/labkey/specimen/actions/SpecimenController.java b/specimen/src/org/labkey/specimen/actions/SpecimenController.java index 0be02ff4..5671e7ef 100644 --- a/specimen/src/org/labkey/specimen/actions/SpecimenController.java +++ b/specimen/src/org/labkey/specimen/actions/SpecimenController.java @@ -5551,7 +5551,7 @@ else if (study.getTimepointType() != TimepointType.VISIT && pd.getVisitDate() != } else { - Visit visit = pd.getSequenceNum() != null ? UpdateSpecimenCommentsBean.getVisitForSequence(study, pd.getSequenceNum()) : null; + Visit visit = pd.getSequenceNum() != null ? StudyInternalService.get().getVisitForSequence(study, pd.getSequenceNum()) : null; ptidVisits.add(new Pair<>(pd.getParticipantId(), visit != null ? visit.getLabel() : "" + StudyInternalService.get().formatSequenceNum(pd.getSequenceNum()))); } } diff --git a/specimen/src/org/labkey/specimen/actions/UpdateSpecimenCommentsBean.java b/specimen/src/org/labkey/specimen/actions/UpdateSpecimenCommentsBean.java index 4a4259a1..a7ea119b 100644 --- a/specimen/src/org/labkey/specimen/actions/UpdateSpecimenCommentsBean.java +++ b/specimen/src/org/labkey/specimen/actions/UpdateSpecimenCommentsBean.java @@ -8,6 +8,7 @@ import org.labkey.api.specimen.Vial; import org.labkey.api.specimen.model.SpecimenComment; import org.labkey.api.study.Study; +import org.labkey.api.study.StudyInternalService; import org.labkey.api.study.StudyService; import org.labkey.api.study.StudyUrls; import org.labkey.api.study.TimepointType; @@ -102,7 +103,7 @@ protected Map> generateParticipantVisitMap(List if (visit != null) { String ptid = vial.getPtid(); - Visit v = getVisitForSequence(study, visit); + Visit v = StudyInternalService.get().getVisitForSequence(study, visit); if (ptid != null && v != null) { if (!pvMap.containsKey(ptid)) @@ -143,15 +144,4 @@ public Map> getParticipantVisitMap() { return _participantVisitMap; } - - public static Visit getVisitForSequence(Study study, double seqNum) - { - List visits = VisitService.get().getVisits(study, Visit.Order.SEQUENCE_NUM); - for (Visit v : visits) - { - if (seqNum >= v.getSequenceNumMinDouble() && seqNum <= v.getSequenceNumMaxDouble()) - return v; - } - return null; - } } From 3eeaabef0b1feaafd33a6212ed6cb0114d021dda Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Mon, 28 Jun 2021 09:50:30 -0700 Subject: [PATCH 342/494] Double -> BigDecimal Original PR: https://github.com/LabKey/platform/pull/2406 --- .../src/org/labkey/specimen/report/SpecimenTypeVisitReport.java | 2 +- .../specimen/report/participant/ParticipantVisitReport.java | 2 +- .../specimen/report/request/RequestParticipantReport.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/specimen/src/org/labkey/specimen/report/SpecimenTypeVisitReport.java b/specimen/src/org/labkey/specimen/report/SpecimenTypeVisitReport.java index 961e135d..76f305b2 100644 --- a/specimen/src/org/labkey/specimen/report/SpecimenTypeVisitReport.java +++ b/specimen/src/org/labkey/specimen/report/SpecimenTypeVisitReport.java @@ -114,7 +114,7 @@ protected String getCellHtml(Visit visit, SummaryByVisitType summary) if (summary == null || summary.getVialCount() == null) return " "; ActionURL link = SpecimenController.getSpecimensURL(_container, true); - link = updateURLFilterParameter(link, "SpecimenDetail.Visit/SequenceNumMin", visit.getSequenceNumMinDouble()); + link = updateURLFilterParameter(link, "SpecimenDetail.Visit/SequenceNumMin", visit.getSequenceNumMin()); link = updateURLFilterParameter(link, "SpecimenDetail.PrimaryType/Description", summary.getPrimaryType()); SpecimenTypeLevel level = getTypeLevelEnum(); diff --git a/specimen/src/org/labkey/specimen/report/participant/ParticipantVisitReport.java b/specimen/src/org/labkey/specimen/report/participant/ParticipantVisitReport.java index 8422fbf1..0f27ed85 100644 --- a/specimen/src/org/labkey/specimen/report/participant/ParticipantVisitReport.java +++ b/specimen/src/org/labkey/specimen/report/participant/ParticipantVisitReport.java @@ -109,7 +109,7 @@ protected String getCellHtml(Visit visit, SummaryByVisitParticipant summary) if (summary == null || summary.getVialCount() == null) return " "; ActionURL link = SpecimenController.getSpecimensURL(_container, true); - link = updateURLFilterParameter(link, "SpecimenDetail.Visit/SequenceNumMin", visit.getSequenceNumMinDouble()); + link = updateURLFilterParameter(link, "SpecimenDetail.Visit/SequenceNumMin", visit.getSequenceNumMin()); link = updateURLFilterParameter(link, "SpecimenDetail." + StudyService.get().getSubjectColumnName(getContainer()), summary.getParticipantId()); String linkHtml = link.getLocalURIString(); if (_filter != null) diff --git a/specimen/src/org/labkey/specimen/report/request/RequestParticipantReport.java b/specimen/src/org/labkey/specimen/report/request/RequestParticipantReport.java index 7a9f383d..75a4f2e3 100644 --- a/specimen/src/org/labkey/specimen/report/request/RequestParticipantReport.java +++ b/specimen/src/org/labkey/specimen/report/request/RequestParticipantReport.java @@ -96,7 +96,7 @@ protected String getCellHtml(Visit visit, RequestSummaryByVisitType summary) if (summary == null || summary.getVialCount() == null) return " "; ActionURL url = SpecimenController.getSpecimensURL(_container, true); - url = updateURLFilterParameter(url, "SpecimenDetail.Visit/SequenceNumMin", visit.getSequenceNumMinDouble()); + url = updateURLFilterParameter(url, "SpecimenDetail.Visit/SequenceNumMin", visit.getSequenceNumMin()); url = updateURLFilterParameter(url, "SpecimenDetail.PrimaryType/Description", summary.getPrimaryType()); SpecimenTypeLevel level = getTypeLevelEnum(); if (level == SpecimenTypeLevel.Derivative || level == SpecimenTypeLevel.Additive) From d1d21c2c16f9a2a4093a5080e4d403fcce79c0b8 Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Thu, 8 Jul 2021 13:42:53 -0700 Subject: [PATCH 343/494] Move all remaining specimen actions (i.e., specimen comments) to specimen module. Eliminate old specimen controller. Original PR: https://github.com/LabKey/platform/pull/2434 --- .../org/labkey/specimen/SpecimenManager.java | 415 ++++++++++++++++++ .../org/labkey/specimen/SpecimenModule.java | 53 +-- .../labkey/specimen/SpecimenServiceImpl.java | 4 +- .../actions/ParticipantCommentAction.java | 177 ++++++++ .../actions/ParticipantCommentForm.java | 74 ++++ .../actions/SpecimenApiController.java | 7 +- .../specimen/actions/SpecimenController.java | 230 +++++++++- .../actions/UpdateSpecimenCommentsBean.java | 3 +- .../ParticipantSiteReportFactory.java | 10 +- .../ParticipantSummaryReportFactory.java | 8 +- .../ParticipantTypeReportFactory.java | 10 +- .../RequestEnrollmentLocationReport.java | 4 +- .../RequestEnrollmentSiteReportFactory.java | 8 +- .../report/request/RequestLocationReport.java | 4 +- .../request/RequestLocationReportFactory.java | 8 +- .../RequestParticipantReportFactory.java | 8 +- .../report/request/RequestReport.java | 4 +- .../report/request/RequestReportFactory.java | 8 +- .../specimentype/TypeCohortReportFactory.java | 8 +- .../TypeParticipantReportFactory.java | 10 +- .../specimentype/TypeReportFactory.java | 2 +- .../TypeSummaryReportFactory.java | 6 +- .../labkey/specimen/view/manageRequest.jsp | 2 +- .../labkey/specimen/view/requestSpecimens.jsp | 2 +- .../src/org/labkey/specimen/view/specimen.jsp | 2 +- 25 files changed, 944 insertions(+), 123 deletions(-) create mode 100644 specimen/src/org/labkey/specimen/SpecimenManager.java create mode 100644 specimen/src/org/labkey/specimen/actions/ParticipantCommentAction.java create mode 100644 specimen/src/org/labkey/specimen/actions/ParticipantCommentForm.java diff --git a/specimen/src/org/labkey/specimen/SpecimenManager.java b/specimen/src/org/labkey/specimen/SpecimenManager.java new file mode 100644 index 00000000..b7779fad --- /dev/null +++ b/specimen/src/org/labkey/specimen/SpecimenManager.java @@ -0,0 +1,415 @@ +package org.labkey.specimen; + +import org.jetbrains.annotations.Nullable; +import org.labkey.api.audit.AuditLogService; +import org.labkey.api.data.ColumnInfo; +import org.labkey.api.data.Container; +import org.labkey.api.data.DbScope; +import org.labkey.api.data.SQLFragment; +import org.labkey.api.data.SimpleFilter; +import org.labkey.api.data.Sort; +import org.labkey.api.data.SqlSelector; +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.ModuleHtmlView; +import org.labkey.api.query.FieldKey; +import org.labkey.api.query.QueryService; +import org.labkey.api.query.UserSchema; +import org.labkey.api.security.User; +import org.labkey.api.specimen.SpecimenCommentAuditEvent; +import org.labkey.api.specimen.SpecimenManagerNew; +import org.labkey.api.specimen.SpecimenQuerySchema; +import org.labkey.api.specimen.SpecimenRequestException; +import org.labkey.api.specimen.SpecimenSchema; +import org.labkey.api.specimen.Vial; +import org.labkey.api.specimen.location.LocationImpl; +import org.labkey.api.specimen.location.LocationManager; +import org.labkey.api.specimen.model.AdditiveType; +import org.labkey.api.specimen.model.DerivativeType; +import org.labkey.api.specimen.model.ExtendedSpecimenRequestView; +import org.labkey.api.specimen.model.SpecimenComment; +import org.labkey.api.study.Cohort; +import org.labkey.api.study.Study; +import org.labkey.api.study.StudyService; +import org.labkey.api.study.Visit; +import org.labkey.api.util.DateUtil; +import org.labkey.api.util.HtmlString; +import org.labkey.api.util.Path; +import org.labkey.api.view.ViewContext; + +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; + +public class SpecimenManager +{ + private final static SpecimenManager INSTANCE = new SpecimenManager(); + + private SpecimenManager() + { + } + + public static SpecimenManager get() + { + return INSTANCE; + } + + public List getVisitsWithSpecimens(Container container, User user, Cohort cohort) + { + Study study = StudyService.get().getStudy(container); + UserSchema schema = SpecimenQuerySchema.get(study, user); + TableInfo tinfo = schema.getTable(SpecimenQuerySchema.SIMPLE_SPECIMEN_TABLE_NAME); + + FieldKey visitKey = FieldKey.fromParts("Visit"); + Map colMap = QueryService.get().getColumns(tinfo, Collections.singleton(visitKey)); + Collection cols = new ArrayList<>(); + cols.add(colMap.get(visitKey)); + Set unresolvedColumns = new HashSet<>(); + cols = QueryService.get().ensureRequiredColumns(tinfo, cols, null, null, unresolvedColumns); + if (!unresolvedColumns.isEmpty()) + throw new IllegalStateException("Unable to resolve column(s): " + unresolvedColumns.toString()); + // generate our select SQL: + SQLFragment specimenSql = Table.getSelectSQL(tinfo, cols, null, null); + + SQLFragment visitIdSQL = new SQLFragment("SELECT DISTINCT Visit FROM (" + specimenSql.getSQL() + ") SimpleSpecimenQuery"); + visitIdSQL.addAll(specimenSql.getParamsArray()); + + List visitIds = new SqlSelector(SpecimenSchema.get().getSchema(), visitIdSQL).getArrayList(Integer.class); + + // Get shared visit study + Study visitStudy = StudyService.get().getStudyForVisits(study); + + SimpleFilter filter = new SimpleFilter(); + filter.addInClause(FieldKey.fromParts("RowId"), visitIds); + if (cohort != null) + filter.addWhereClause("CohortId IS NULL OR CohortId = ?", new Object[] { cohort.getRowId() }); + return StudyService.get().getVisits(visitStudy, filter, new Sort("DisplayOrder,SequenceNumMin")); + } + + public LocationImpl[] getSitesWithRequests(Container container) + { + TableInfo locationTableInfo = SpecimenSchema.get().getTableInfoLocation(container); + SQLFragment sql = new SQLFragment("SELECT * FROM " + locationTableInfo.getSelectName() + " WHERE rowid IN\n" + + "(SELECT destinationsiteid FROM study.samplerequest WHERE container = ?)\n" + + "AND container = ? ORDER BY label", container.getId(), container.getId()); + + return new SqlSelector(SpecimenSchema.get().getSchema(), sql).getArray(LocationImpl.class); + } + + public Set getEnrollmentSitesWithRequests(Container container, User user) + { + UserSchema schema = SpecimenQuerySchema.get(StudyService.get().getStudy(container), user); + TableInfo tableInfoSpecimenDetail = schema.getTable(SpecimenQuerySchema.SPECIMEN_WRAP_TABLE_NAME); + if (null == tableInfoSpecimenDetail) + throw new IllegalStateException("SpecimenDetail table not found."); + String tableInfoAlias = "Specimen"; + SQLFragment sql = new SQLFragment("SELECT Participant.EnrollmentSiteId FROM "); + sql.append(tableInfoSpecimenDetail.getFromSQL(tableInfoAlias)).append(", ") + .append("study.SampleRequestSpecimen AS RequestSpecimen, \n" + + "study.SampleRequest AS Request, study.SampleRequestStatus AS Status,\n" + + "study.Participant AS Participant\n" + + "WHERE Request.Container = Status.Container AND\n" + + "\tRequest.StatusId = Status.RowId AND\n" + + "\tRequestSpecimen.SampleRequestId = Request.RowId AND\n" + + "\tRequestSpecimen.Container = Request.Container AND\n" + + "\tSpecimen.Container = RequestSpecimen.Container AND\n" + + "\tSpecimen.GlobalUniqueId = RequestSpecimen.SpecimenGlobalUniqueId AND\n" + + "\tParticipant.EnrollmentSiteId IS NOT NULL AND\n" + + "\tParticipant.Container = Specimen.Container AND\n" + + "\tParticipant.ParticipantId = Specimen.Ptid AND\n" + + "\tStatus.SpecimensLocked = ? AND\n" + + "\tRequest.Container = ?"); + sql.add(Boolean.TRUE); + sql.add(container); + + return getSitesWithIdSql(container, "EnrollmentSiteId", sql); + } + + public Set getEnrollmentSitesWithSpecimens(Container container, User user) + { + UserSchema schema = SpecimenQuerySchema.get(StudyService.get().getStudy(container), user); + TableInfo tableInfoSpecimenDetail = schema.getTable(SpecimenQuerySchema.SPECIMEN_WRAP_TABLE_NAME); + if (null == tableInfoSpecimenDetail) + throw new IllegalStateException("SpecimenDetail table not found."); + String tableInfoAlias = "Specimen"; + SQLFragment sql = new SQLFragment("SELECT EnrollmentSiteId FROM "); + sql.append(tableInfoSpecimenDetail.getFromSQL(tableInfoAlias)).append(", study.Participant AS Participant\n" + + "WHERE Specimen.Ptid = Participant.ParticipantId AND\n" + + "\tParticipant.EnrollmentSiteId IS NOT NULL AND\n" + + "\tSpecimen.Container = Participant.Container AND\n" + + "\tSpecimen.Container = ?\n" + + "GROUP BY EnrollmentSiteId"); + sql.add(container); + + return getSitesWithIdSql(container, "EnrollmentSiteId", sql); + } + + private Set getSitesWithIdSql(final Container container, final String idColumnName, SQLFragment sql) + { + final Set locations = new TreeSet<>((s1, s2) -> + { + if (s1 == null && s2 == null) + return 0; + if (s1 == null) + return -1; + if (s2 == null) + return 1; + return s1.getLabel().compareTo(s2.getLabel()); + }); + + new SqlSelector(SpecimenSchema.get().getSchema(), sql).forEach(rs -> { + // try getObject first to see if we have a value for our row; getInt will coerce the null to + // zero, which could (theoretically) be a valid site ID. + if (rs.getObject(idColumnName) == null) + locations.add(null); + else + locations.add(LocationManager.get().getLocation(container, rs.getInt(idColumnName))); + }); + + return locations; + } + + private static final int GET_COMMENT_BATCH_SIZE = 1000; + + public Map getSpecimenComments(List vials) + { + if (vials == null || vials.size() == 0) + return Collections.emptyMap(); + + Container container = vials.get(0).getContainer(); + final Map result = new HashMap<>(); + int offset = 0; + + while (offset < vials.size()) + { + final Map idToVial = new HashMap<>(); + + for (int current = offset; current < offset + GET_COMMENT_BATCH_SIZE && current < vials.size(); current++) + { + Vial vial = vials.get(current); + idToVial.put(vial.getGlobalUniqueId(), vial); + if (!container.equals(vial.getContainer())) + throw new IllegalArgumentException("All specimens must be from the same container"); + } + + SimpleFilter filter = SimpleFilter.createContainerFilter(container); + filter.addInClause(FieldKey.fromParts("GlobalUniqueId"), idToVial.keySet()); + + new TableSelector(SpecimenSchema.get().getTableInfoSpecimenComment(), filter, null).forEach(SpecimenComment.class, comment -> { + Vial vial = idToVial.get(comment.getGlobalUniqueId()); + result.put(vial, comment); + }); + + offset += GET_COMMENT_BATCH_SIZE; + } + + return result; + } + + public SpecimenComment getSpecimenCommentForVial(Container container, String globalUniqueId) + { + SimpleFilter filter = SimpleFilter.createContainerFilter(container); + filter.addCondition(FieldKey.fromParts("GlobalUniqueId"), globalUniqueId); + + return new TableSelector(SpecimenSchema.get().getTableInfoSpecimenComment(), filter, null).getObject(SpecimenComment.class); + } + + public SpecimenComment getSpecimenCommentForVial(Vial vial) + { + return getSpecimenCommentForVial(vial.getContainer(), vial.getGlobalUniqueId()); + } + + public SpecimenComment setSpecimenComment(User user, Vial vial, String commentText, boolean qualityControlFlag, boolean qualityControlFlagForced) + { + TableInfo commentTable = SpecimenSchema.get().getTableInfoSpecimenComment(); + DbScope scope = commentTable.getSchema().getScope(); + SpecimenComment comment = getSpecimenCommentForVial(vial); + boolean clearComment = commentText == null && !qualityControlFlag && !qualityControlFlagForced; + try (DbScope.Transaction transaction = scope.ensureTransaction()) + { + SpecimenComment result; + if (clearComment) + { + if (comment != null) + { + Table.delete(commentTable, comment.getRowId()); + auditSpecimenComment(user, vial, comment.getComment(), null, comment.isQualityControlFlag(), false); + } + result = null; + } + else + { + if (comment != null) + { + String prevComment = comment.getComment(); + boolean prevConflictState = comment.isQualityControlFlag(); + comment.setComment(commentText); + comment.setQualityControlFlag(qualityControlFlag); + comment.setQualityControlFlagForced(qualityControlFlagForced); + comment.beforeUpdate(user); + result = Table.update(user, commentTable, comment, comment.getRowId()); + auditSpecimenComment(user, vial, prevComment, result.getComment(), prevConflictState, result.isQualityControlFlag()); + } + else + { + comment = new SpecimenComment(); + comment.setGlobalUniqueId(vial.getGlobalUniqueId()); + comment.setSpecimenHash(vial.getSpecimenHash()); + comment.setComment(commentText); + comment.setQualityControlFlag(qualityControlFlag); + comment.setQualityControlFlagForced(qualityControlFlagForced); + comment.beforeInsert(user, vial.getContainer().getId()); + result = Table.insert(user, commentTable, comment); + auditSpecimenComment(user, vial, null, result.getComment(), false, comment.isQualityControlFlag()); + } + } + transaction.commit(); + return result; + } + } + + @Nullable + public ExtendedSpecimenRequestView getExtendedSpecimenRequestView(ViewContext context) + { + if (context == null || context.getContainer() == null) + return null; + + Path path = ModuleHtmlView.getStandardPath("extendedrequest"); + + for (Module module : context.getContainer().getActiveModules()) + { + if (ModuleHtmlView.exists(module, path)) + { + ModuleHtmlView moduleView = ModuleHtmlView.get(module, path); + assert null != moduleView; + HtmlString html = moduleView.getHtml(); + String s = ModuleHtmlView.replaceTokens(html.toString(), context); + return ExtendedSpecimenRequestView.createView(s); + } + } + + return null; + } + + private boolean safeComp(Object a, Object b) + { + if (a == null && b == null) + return true; + if (a == null || b == null) + return false; + return a.equals(b); + } + + private void auditSpecimenComment(User user, Vial vial, String oldComment, String newComment, boolean prevConflictState, boolean newConflictState) + { + String verb = "updated"; + if (oldComment == null) + verb = "added"; + else if (newComment == null) + verb = "deleted"; + String message = ""; + if (!safeComp(oldComment, newComment)) + { + message += "Comment " + verb + ".\n"; + if (oldComment != null) + message += "Previous value: " + oldComment + "\n"; + if (newComment != null) + message += "New value: " + newComment + "\n"; + } + + if (!safeComp(prevConflictState, newConflictState)) + { + message = "QC alert flag changed.\n"; + if (oldComment != null) + message += "Previous value: " + prevConflictState + "\n"; + if (newComment != null) + message += "New value: " + newConflictState + "\n"; + } + + SpecimenCommentAuditEvent event = new SpecimenCommentAuditEvent(vial.getContainer().getId(), message); + event.setVialId(vial.getGlobalUniqueId()); + + AuditLogService.get().addEvent(user, event); + } + + public List getVials(Container container, User user, String participantId, Double visit) + { + SimpleFilter filter = SimpleFilter.createContainerFilter(container); + filter.addClause(new SimpleFilter.SQLClause("LOWER(ptid) = LOWER(?)", new Object[] {participantId}, FieldKey.fromParts("ptid"))); + filter.addCondition(FieldKey.fromParts("VisitValue"), visit); + return SpecimenManagerNew.get().getVials(container, user, filter); + } + + public List getVials(Container container, User user, int[] vialsRowIds) + { + Set uniqueRowIds = new HashSet<>(vialsRowIds.length); + for (int vialRowId : vialsRowIds) + uniqueRowIds.add((long)vialRowId); + return SpecimenManagerNew.get().getVials(container, user, uniqueRowIds); + } + + public List getVials(Container container, User user, String[] globalUniqueIds) throws SpecimenRequestException + { + SimpleFilter filter = new SimpleFilter(); + Set uniqueRowIds = new HashSet<>(globalUniqueIds.length); + Collections.addAll(uniqueRowIds, globalUniqueIds); + List ids = new ArrayList<>(uniqueRowIds); + filter.addInClause(FieldKey.fromParts("GlobalUniqueId"), ids); + List vials = SpecimenManagerNew.get().getVials(container, user, filter); + if (vials == null || vials.size() != ids.size()) + throw new SpecimenRequestException("Vial not found."); // an id has no matching specimen, let caller determine what to report + return vials; + } + + public List getVials(Container container, User user, String participantId, Date date) + { + SimpleFilter filter = new SimpleFilter(new SimpleFilter.SQLClause("LOWER(ptid) = LOWER(?)", new Object[] {participantId}, FieldKey.fromParts("ptid"))); + Calendar endCal = DateUtil.newCalendar(date.getTime()); + endCal.add(Calendar.DATE, 1); + filter.addClause(new SimpleFilter.SQLClause("DrawTimestamp >= ? AND DrawTimestamp < ?", new Object[] {date, endCal.getTime()})); + return SpecimenManagerNew.get().getVials(container, user, filter); + } + + @Nullable + public AdditiveType getAdditiveType(Container c, int rowId) + { + List additiveTypes = getAdditiveTypes(c, new SimpleFilter(FieldKey.fromParts("RowId"), rowId)); + if (!additiveTypes.isEmpty()) + return additiveTypes.get(0); + return null; + } + + private List getAdditiveTypes(final Container container, @Nullable SimpleFilter filter) + { + final List additiveTypes = new ArrayList<>(); + new TableSelector(SpecimenSchema.get().getTableInfoSpecimenAdditive(container), filter, null). + forEachMap(map -> additiveTypes.add(new AdditiveType(container, map))); + return additiveTypes; + } + + public DerivativeType getDerivativeType(Container c, int rowId) + { + List derivativeTypes = getDerivativeTypes(c, new SimpleFilter(FieldKey.fromParts("RowId"), rowId)); + if (!derivativeTypes.isEmpty()) + return derivativeTypes.get(0); + return null; + } + + private List getDerivativeTypes(final Container container, @Nullable SimpleFilter filter) + { + final List derivativeTypes = new ArrayList<>(); + new TableSelector(SpecimenSchema.get().getTableInfoSpecimenDerivative(container), filter, null). + forEachMap(map -> derivativeTypes.add(new DerivativeType(container, map))); + return derivativeTypes; + } +} diff --git a/specimen/src/org/labkey/specimen/SpecimenModule.java b/specimen/src/org/labkey/specimen/SpecimenModule.java index 0faf3837..191fc7a0 100644 --- a/specimen/src/org/labkey/specimen/SpecimenModule.java +++ b/specimen/src/org/labkey/specimen/SpecimenModule.java @@ -22,9 +22,12 @@ import org.labkey.api.audit.AuditLogService; import org.labkey.api.data.Container; import org.labkey.api.data.ContainerManager; +import org.labkey.api.data.TableInfo; import org.labkey.api.module.ModuleContext; import org.labkey.api.module.SpringModule; import org.labkey.api.pipeline.PipelineService; +import org.labkey.api.query.QueryParam; +import org.labkey.api.query.QueryView; import org.labkey.api.security.roles.RoleManager; import org.labkey.api.specimen.SpecimenMigrationService; import org.labkey.api.specimen.SpecimenRequestManager; @@ -42,7 +45,6 @@ import org.labkey.api.view.ActionURL; import org.labkey.api.view.HttpView; import org.labkey.api.view.WebPartFactory; -import org.labkey.specimen.actions.ShowUploadSpecimensAction; import org.labkey.specimen.actions.SpecimenApiController; import org.labkey.specimen.actions.SpecimenController; import org.labkey.specimen.importer.DefaultSpecimenImportStrategyFactory; @@ -61,7 +63,6 @@ import org.labkey.specimen.writer.SpecimenArchiveWriter; import org.labkey.specimen.writer.SpecimenSettingsWriter; import org.labkey.specimen.writer.SpecimenWriter; -import org.springframework.web.servlet.mvc.Controller; import java.util.Collection; import java.util.Collections; @@ -127,18 +128,6 @@ public ActionURL getBeginURL(Container c) return new ActionURL(SpecimenController.BeginAction.class, c); } - @Override - public ActionURL getManageRequestStatusURL(Container c, int requestId) - { - return SpecimenController.getManageRequestStatusURL(c, requestId); - } - - @Override - public ActionURL getManageRequestURL(Container c, int requestId, @Nullable ActionURL returnUrl) - { - return SpecimenController.getManageRequestURL(c, requestId, returnUrl); - } - @Override public ActionURL getSelectedSpecimensURL(Container c) { @@ -157,12 +146,6 @@ public ActionURL getSpecimensURL(Container c) return SpecimenController.getSpecimensURL(c); } - @Override - public ActionURL getSpecimensURL(Container c, boolean showVials) - { - return SpecimenController.getSpecimensURL(c, showVials); - } - @Override public ActionURL getSpecimenEventsURL(Container c, ActionURL returnURL) { @@ -170,33 +153,23 @@ public ActionURL getSpecimenEventsURL(Container c, ActionURL returnURL) } @Override - public ActionURL getUploadSpecimensURL(Container c) + public ActionURL getInsertSpecimenQueryRowURL(Container c, String schemaName, TableInfo table) { - return new ActionURL(ShowUploadSpecimensAction.class, c); - } + ActionURL url = new ActionURL(SpecimenController.InsertSpecimenQueryRowAction.class, c); + url.addParameter("schemaName", schemaName); + url.addParameter(QueryView.DATAREGIONNAME_DEFAULT + "." + QueryParam.queryName, table.getName()); - @Override - public ActionURL getViewRequestsURL(Container c) - { - return new ActionURL(SpecimenController.ViewRequestsAction.class, c); + return url; } @Override - public Class getClearCommentsActionClass() + public ActionURL getUpdateSpecimenQueryRowURL(Container c, String schemaName, TableInfo table) { - return SpecimenController.ClearCommentsAction.class; - } + ActionURL url = new ActionURL(SpecimenController.UpdateSpecimenQueryRowAction.class, c); + url.addParameter("schemaName", schemaName); + url.addParameter(QueryView.DATAREGIONNAME_DEFAULT + "." + QueryParam.queryName, table.getName()); - @Override - public Class getShowCreateSpecimenRequestActionClass() - { - return SpecimenController.ShowCreateSpecimenRequestAction.class; - } - - @Override - public Class getUpdateCommentsActionClass() - { - return SpecimenController.UpdateCommentsAction.class; + return url; } }); } diff --git a/specimen/src/org/labkey/specimen/SpecimenServiceImpl.java b/specimen/src/org/labkey/specimen/SpecimenServiceImpl.java index cf80ff5d..e190e53d 100644 --- a/specimen/src/org/labkey/specimen/SpecimenServiceImpl.java +++ b/specimen/src/org/labkey/specimen/SpecimenServiceImpl.java @@ -225,7 +225,7 @@ public Set getSampleInfo(Container studyContainer, User user, { if (null != studyContainer && null != StringUtils.trimToNull(participantId) && null != date) { - List matches = SpecimenManagerNew.get().getVials(studyContainer, user, participantId, date); + List matches = SpecimenManager.get().getVials(studyContainer, user, participantId, date); if (matches.size() > 0) { Set result = new HashSet<>(); @@ -245,7 +245,7 @@ public Set getSampleInfo(Container studyContainer, User user, { if (null != studyContainer && null != StringUtils.trimToNull(participantId) && null != visit) { - List matches = SpecimenManagerNew.get().getVials(studyContainer, user, participantId, visit); + List matches = SpecimenManager.get().getVials(studyContainer, user, participantId, visit); if (matches.size() > 0) { Set result = new HashSet<>(); diff --git a/specimen/src/org/labkey/specimen/actions/ParticipantCommentAction.java b/specimen/src/org/labkey/specimen/actions/ParticipantCommentAction.java new file mode 100644 index 00000000..6277560b --- /dev/null +++ b/specimen/src/org/labkey/specimen/actions/ParticipantCommentAction.java @@ -0,0 +1,177 @@ +/* + * 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.specimen.actions; + +import org.apache.commons.lang3.StringUtils; +import org.labkey.api.data.ColumnInfo; +import org.labkey.api.data.Container; +import org.labkey.api.query.QueryUpdateForm; +import org.labkey.api.security.RequiresPermission; +import org.labkey.api.security.User; +import org.labkey.api.security.permissions.InsertPermission; +import org.labkey.api.security.permissions.ReadPermission; +import org.labkey.api.specimen.SpecimenManagerNew; +import org.labkey.api.specimen.Vial; +import org.labkey.api.specimen.model.SpecimenComment; +import org.labkey.api.study.InsertUpdateAction; +import org.labkey.api.study.Study; +import org.labkey.api.study.StudyService; +import org.labkey.api.view.DataView; +import org.labkey.api.view.InsertView; +import org.labkey.api.view.NavTree; +import org.labkey.api.view.UpdateView; +import org.labkey.specimen.SpecimenManager; +import org.springframework.validation.BindException; +import org.springframework.web.servlet.ModelAndView; + +import java.util.Map; + +/** + * User: klum + * Date: Oct 7, 2009 + */ +@RequiresPermission(ReadPermission.class) +public abstract class ParticipantCommentAction extends InsertUpdateAction +{ + public ParticipantCommentAction() + { + super(ParticipantCommentForm.class); + } + + @Override + protected void addExtraNavTrail(NavTree root) + { + } + + @Override + public boolean handlePost(ParticipantCommentForm form, BindException errors) throws Exception + { + if (super.handlePost(form, errors)) + { + // clear any vial comments specified + User user = getUser(); + Container container = getContainer(); + for (int rowId : form.getVialCommentsToClear()) + { + Vial vial = SpecimenManagerNew.get().getVial(container, user, rowId); + if (vial != null) + { + SpecimenComment comment = SpecimenManager.get().getSpecimenCommentForVial(vial); + if (comment != null) + { + SpecimenManager.get().setSpecimenComment(user, vial, null, + comment.isQualityControlFlag(), comment.isQualityControlFlagForced()); + } + } + } + return true; + } + return false; + } + + @RequiresPermission(ReadPermission.class) + public static class SpecimenCommentInsertAction extends ParticipantCommentAction + { + private DataView _dataView; + + @Override + public ModelAndView getView(final ParticipantCommentForm form, boolean reshow, BindException errors) throws Exception + { + // super.getView() checks permissions + ModelAndView view = super.getView(form, reshow, errors); + if (!reshow && _dataView instanceof InsertView) + { + ((InsertView)_dataView).setInitialValue(StudyService.get().getSubjectColumnName(getContainer()), form.getParticipantId()); + ((InsertView)_dataView).setInitialValue("SequenceNum", form.getVisitId()); + + if (!StringUtils.isBlank(form.getComment())) + { + Study study = getStudy(); + String commentProperty = form.getVisitId() != 0 ? study.getParticipantVisitCommentProperty() : + study.getParticipantCommentProperty(); + + ((InsertView)_dataView).setInitialValue(ColumnInfo.legalNameFromName(commentProperty), form.getComment()); + } + for (int rowId : form.getVialCommentsToClear()) + _dataView.getDataRegion().addHiddenFormField(ParticipantCommentForm.params.vialCommentsToClear, String.valueOf(rowId)); + } + return view; + } + + @Override + protected DataView createNewView(ParticipantCommentForm form, QueryUpdateForm updateForm, BindException errors) + { + _dataView = super.createNewView(form, updateForm, errors); + return _dataView; + } + + @Override + protected boolean isInsert() + { + return true; + } + } + + @RequiresPermission(InsertPermission.class) + public static class SpecimenCommentUpdateAction extends ParticipantCommentAction + { + @Override + protected DataView createNewView(final ParticipantCommentForm form, QueryUpdateForm updateForm, BindException errors) + { + try + { + if (!StringUtils.isBlank(form.getComment())) + { + updateForm.refreshFromDb(); + + Study study = getStudy(); + String commentProperty = form.getVisitId() != 0 ? study.getParticipantVisitCommentProperty() : + study.getParticipantCommentProperty(); + commentProperty = ColumnInfo.legalNameFromName(commentProperty); + + Object values = updateForm.getOldValues(); + if (values instanceof Map) + { + Map valueMap = (Map)values; + String oldComment = String.valueOf(valueMap.get(commentProperty)); + String newComment = form.getComment(); + + if (!StringUtils.isBlank(oldComment) && !StringUtils.equals(oldComment, form.getComment())) + newComment = oldComment + " : " + form.getComment(); + valueMap.put(commentProperty, newComment); + } + } + DataView view = new UpdateView(updateForm, errors); + + for (int rowId : form.getVialCommentsToClear()) + view.getDataRegion().addHiddenFormField(ParticipantCommentForm.params.vialCommentsToClear, String.valueOf(rowId)); + + return view; + } + catch (Exception e) + { + throw new RuntimeException(e); + } + } + + @Override + protected boolean isInsert() + { + return false; + } + } +} diff --git a/specimen/src/org/labkey/specimen/actions/ParticipantCommentForm.java b/specimen/src/org/labkey/specimen/actions/ParticipantCommentForm.java new file mode 100644 index 00000000..16eed5f2 --- /dev/null +++ b/specimen/src/org/labkey/specimen/actions/ParticipantCommentForm.java @@ -0,0 +1,74 @@ +package org.labkey.specimen.actions; + +import org.labkey.api.study.EditDatasetRowForm; + +public class ParticipantCommentForm extends EditDatasetRowForm +{ + private String _participantId; + private int _visitId; + private String _comment; + private String _oldComment; + private int[] _vialCommentsToClear = new int[0]; + + public enum params + { + participantId, + visitId, + comment, + oldComment, + vialCommentsToClear, + lsid, + datasetId, + returnUrl, + } + + public String getParticipantId() + { + return _participantId; + } + + public void setParticipantId(String participantId) + { + _participantId = participantId; + } + + public int getVisitId() + { + return _visitId; + } + + public void setVisitId(int visitId) + { + _visitId = visitId; + } + + public String getComment() + { + return _comment; + } + + public void setComment(String comment) + { + _comment = comment; + } + + public String getOldComment() + { + return _oldComment; + } + + public void setOldComment(String oldComment) + { + _oldComment = oldComment; + } + + public int[] getVialCommentsToClear() + { + return _vialCommentsToClear; + } + + public void setVialCommentsToClear(int[] vialsCommentsToClear) + { + _vialCommentsToClear = vialsCommentsToClear; + } +} diff --git a/specimen/src/org/labkey/specimen/actions/SpecimenApiController.java b/specimen/src/org/labkey/specimen/actions/SpecimenApiController.java index 6bd96f7b..5eac6eda 100644 --- a/specimen/src/org/labkey/specimen/actions/SpecimenApiController.java +++ b/specimen/src/org/labkey/specimen/actions/SpecimenApiController.java @@ -52,6 +52,7 @@ import org.labkey.api.util.PageFlowUtil; import org.labkey.api.view.NotFoundException; import org.labkey.api.view.ViewContext; +import org.labkey.specimen.SpecimenManager; import org.springframework.validation.BindException; import java.util.ArrayList; @@ -132,14 +133,14 @@ private List> getSpecimenListResponse(List vials) vialProperties.put("derivativeTypeId", vial.getDerivativeTypeId()); if (vial.getDerivativeTypeId() != null) { - DerivativeType derivativeType = SpecimenManagerNew.get().getDerivativeType(vial.getContainer(), vial.getDerivativeTypeId()); + DerivativeType derivativeType = SpecimenManager.get().getDerivativeType(vial.getContainer(), vial.getDerivativeTypeId()); if (derivativeType != null) vialProperties.put("derivativeType", derivativeType.getDerivative()); } vialProperties.put("additiveTypeId", vial.getAdditiveTypeId()); if (vial.getAdditiveTypeId() != null) { - AdditiveType additiveType = SpecimenManagerNew.get().getAdditiveType(vial.getContainer(), vial.getAdditiveTypeId()); + AdditiveType additiveType = SpecimenManager.get().getAdditiveType(vial.getContainer(), vial.getAdditiveTypeId()); if (additiveType != null) vialProperties.put("additiveType", additiveType.getAdditive()); } @@ -356,7 +357,7 @@ public ApiResponse execute(GetVialsByRowIdForm form, BindException errors) { try { - List vials = SpecimenManagerNew.get().getVials(container, form.getViewContext().getUser(), form.getRowIds()); + List vials = SpecimenManager.get().getVials(container, form.getViewContext().getUser(), form.getRowIds()); vialList = getSpecimenListResponse(vials); } catch (SpecimenRequestException e) diff --git a/specimen/src/org/labkey/specimen/actions/SpecimenController.java b/specimen/src/org/labkey/specimen/actions/SpecimenController.java index 5671e7ef..b499cff6 100644 --- a/specimen/src/org/labkey/specimen/actions/SpecimenController.java +++ b/specimen/src/org/labkey/specimen/actions/SpecimenController.java @@ -1,6 +1,8 @@ package org.labkey.specimen.actions; import org.apache.commons.lang3.StringUtils; +import org.apache.log4j.LogManager; +import org.apache.log4j.Logger; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.json.JSONObject; @@ -37,6 +39,9 @@ import org.labkey.api.query.FieldKey; import org.labkey.api.query.QueryDefinition; import org.labkey.api.query.QueryService; +import org.labkey.api.query.QueryUpdateForm; +import org.labkey.api.query.QueryView; +import org.labkey.api.query.UserSchemaAction; import org.labkey.api.query.ValidationException; import org.labkey.api.reader.ColumnDescriptor; import org.labkey.api.reader.DataLoader; @@ -50,16 +55,13 @@ import org.labkey.api.specimen.AmbiguousLocationException; import org.labkey.api.specimen.RequestEventType; import org.labkey.api.specimen.RequestedSpecimens; -import org.labkey.api.specimen.SpecimenManager; import org.labkey.api.specimen.SpecimenManagerNew; -import org.labkey.api.specimen.SpecimenMigrationService; import org.labkey.api.specimen.SpecimenQuerySchema; import org.labkey.api.specimen.SpecimenRequestException; import org.labkey.api.specimen.SpecimenRequestManager; import org.labkey.api.specimen.SpecimenRequestStatus; import org.labkey.api.specimen.SpecimenSchema; import org.labkey.api.specimen.Vial; -import org.labkey.api.specimen.actions.ParticipantCommentForm; import org.labkey.api.specimen.importer.RequestabilityManager; import org.labkey.api.specimen.importer.SimpleSpecimenImporter; import org.labkey.api.specimen.location.LocationImpl; @@ -75,6 +77,7 @@ import org.labkey.api.specimen.requirements.SpecimenRequestRequirement; import org.labkey.api.specimen.requirements.SpecimenRequestRequirementProvider; import org.labkey.api.specimen.requirements.SpecimenRequestRequirementType; +import org.labkey.api.specimen.security.permissions.EditSpecimenDataPermission; import org.labkey.api.specimen.security.permissions.ManageRequestSettingsPermission; import org.labkey.api.specimen.security.permissions.ManageRequestsPermission; import org.labkey.api.specimen.security.permissions.RequestSpecimensPermission; @@ -119,15 +122,19 @@ import org.labkey.api.view.GridView; import org.labkey.api.view.HBox; import org.labkey.api.view.HtmlView; +import org.labkey.api.view.HttpView; +import org.labkey.api.view.InsertView; import org.labkey.api.view.JspView; import org.labkey.api.view.NavTree; import org.labkey.api.view.NotFoundException; import org.labkey.api.view.RedirectException; import org.labkey.api.view.UnauthorizedException; +import org.labkey.api.view.UpdateView; import org.labkey.api.view.VBox; import org.labkey.api.view.ViewBackgroundInfo; import org.labkey.api.view.ViewContext; import org.labkey.api.view.WebPartView; +import org.labkey.specimen.SpecimenManager; import org.labkey.specimen.notifications.ActorNotificationRecipientSet; import org.labkey.specimen.pipeline.SpecimenArchive; import org.labkey.specimen.pipeline.SpecimenBatch; @@ -161,6 +168,7 @@ import java.io.OutputStream; import java.io.Writer; import java.nio.charset.StandardCharsets; +import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Timestamp; import java.util.ArrayList; @@ -177,6 +185,7 @@ public class SpecimenController extends SpringActionController { + private static final Logger LOG = LogManager.getLogger(SpecimenController.class); private static final DefaultActionResolver _resolver = new DefaultActionResolver( SpecimenController.class, @@ -195,9 +204,39 @@ public class SpecimenController extends SpringActionController SpecimenReportActions.RequestParticipantReportAction.class, SpecimenReportActions.TypeParticipantReportAction.class, SpecimenReportActions.TypeSummaryReportAction.class, - SpecimenReportActions.TypeCohortReportAction.class + SpecimenReportActions.TypeCohortReportAction.class, + + ParticipantCommentAction.SpecimenCommentInsertAction.class, + ParticipantCommentAction.SpecimenCommentUpdateAction.class ); + public static class SpecimenUrlsImpl implements SpecimenUrls + { + @Override + public ActionURL getSpecimensURL(Container c) + { + return SpecimenController.getSpecimensURL(c); + } + + @Override + public ActionURL getSpecimensURL(Container c, boolean showVials) + { + return SpecimenController.getSpecimensURL(c, showVials); + } + + @Override + public ActionURL getManageRequestURL(Container c, int requestId) + { + return SpecimenController.getManageRequestURL(c, requestId, null); + } + + @Override + public ActionURL getManageRequestStatusURL(Container c, int requestId) + { + return SpecimenController.getManageRequestStatusURL(c, requestId); + } + } + private Study _study = null; public SpecimenController() @@ -251,7 +290,7 @@ public void addRootNavTrail(NavTree root) ActionURL rootURL; FolderType folderType = c.getFolderType(); Module module = folderType.getDefaultModule(); - if (null != module && "study".equals(folderType.getDefaultModule().getName())) + if (null != module && "study".equals(module.getName())) { rootURL = folderType.getStartURL(c, getUser()); } @@ -354,13 +393,13 @@ public SpecimenQueryView getSpecimenQueryView(boolean showVials, boolean forExpo if (settings.isEnableRequests()) { MenuButton requestMenuButton = new MenuButton("Request Options"); - requestMenuButton.addMenuItem("View Existing Requests", SpecimenMigrationService.get().getViewRequestsURL(getContainer())); + requestMenuButton.addMenuItem("View Existing Requests", new ActionURL(SpecimenController.ViewRequestsAction.class, getContainer())); if (!commentsMode) { if (getViewContext().getContainer().hasPermission(getViewContext().getUser(), RequestSpecimensPermission.class)) { final String jsRegionObject = DataRegion.getJavaScriptObjectReference(gridView.getSettings().getDataRegionName()); - String createRequestURL = urlFor(SpecimenMigrationService.get().getShowCreateSpecimenRequestActionClass()).addReturnURL(getViewContext().getActionURL()).toString(); + String createRequestURL = urlFor(ShowCreateSpecimenRequestAction.class).addReturnURL(getViewContext().getActionURL()).toString(); requestMenuButton.addMenuItem("Create New Request", "if (verifySelected(" + jsRegionObject + ".form, '" + createRequestURL + @@ -393,13 +432,13 @@ public SpecimenQueryView getSpecimenQueryView(boolean showVials, boolean forExpo { MenuButton commentsMenuButton = new MenuButton("Comments" + (manualQCEnabled ? " and QC" : "")); final String jsRegionObject = DataRegion.getJavaScriptObjectReference(gridView.getSettings().getDataRegionName()); - String setCommentsURL = urlFor(SpecimenMigrationService.get().getUpdateCommentsActionClass()).toString(); + String setCommentsURL = urlFor(UpdateCommentsAction.class).toString(); NavTree setItem = commentsMenuButton.addMenuItem("Set Vial Comment " + (manualQCEnabled ? "or QC State " : "") + "for Selected", "if (verifySelected(" + jsRegionObject + ".form, '" + setCommentsURL + "', 'post', 'rows')) " + jsRegionObject + ".form.submit(); return false;"); setItem.setId("Comments:Set"); - String clearCommentsURL = urlFor(SpecimenMigrationService.get().getClearCommentsActionClass()).toString(); + String clearCommentsURL = urlFor(ClearCommentsAction.class).toString(); NavTree clearItem = commentsMenuButton.addMenuItem("Clear Vial Comments for Selected", "if (verifySelected(" + jsRegionObject + ".form, '" + clearCommentsURL + "', 'post', 'rows') && confirm('This will permanently clear comments for all selected vials. " + @@ -465,8 +504,8 @@ public SpecimenQueryView getSpecimenQueryView(boolean showVials, boolean forExpo if (getViewContext().hasPermission(AdminPermission.class)) { Button upload = new Button.ButtonBuilder("Import Specimens") - .href(SpecimenMigrationService.get().getUploadSpecimensURL(getContainer())) - .build(); + .href(new ActionURL(ShowUploadSpecimensAction.class, getContainer())) + .build(); buttons.add(upload); } @@ -3524,11 +3563,10 @@ protected int importData(DataLoader dl, FileStream file, String originalName, Ba { // Get all the specimen objects. If it throws an exception then there was an error and we'll // root around to figure out what to report - SpecimenManagerNew specimenManager = SpecimenManagerNew.get(); try { SpecimenRequest request = SpecimenRequestManager.get().getRequest(getContainer(), _requestId); - List vials = SpecimenManagerNew.get().getVials(getContainer(), getUser(), globalIds); + List vials = SpecimenManager.get().getVials(getContainer(), getUser(), globalIds); if (vials != null && vials.size() == globalIds.length) { @@ -3568,7 +3606,7 @@ protected int importData(DataLoader dl, FileStream file, String originalName, Ba boolean hasSpecimenError = false; for (String id : globalIds) { - Vial vial = specimenManager.getVial(getContainer(), getUser(), id); + Vial vial = SpecimenManagerNew.get().getVial(getContainer(), getUser(), id); if (vial == null) { errorList.add("Specimen " + id + " not found."); @@ -4082,7 +4120,7 @@ public ModelAndView getView(UpdateParticipantCommentsForm specimenCommentsForm, { try { - List vials = SpecimenManagerNew.get().getVials(getContainer(), getUser(), rowId); + List vials = SpecimenManager.get().getVials(getContainer(), getUser(), rowId); selectedVials = new ArrayList<>(vials); } catch (SpecimenRequestException e) @@ -4120,19 +4158,19 @@ public boolean handlePost(UpdateParticipantCommentsForm commentsForm, BindExcept Vial vial = SpecimenManagerNew.get().getVial(container, user, commentsForm.getCopySampleId()); if (vial != null) { - _successUrl = new ActionURL(urlProvider(SpecimenUrls.class).getCopyParticipantCommentActionClass(), container). - addParameter(ParticipantCommentForm.params.participantId, vial.getPtid()). - addParameter(ParticipantCommentForm.params.visitId, String.valueOf(vial.getVisitValue())). - addParameter(ParticipantCommentForm.params.comment, commentsForm.getComments()). - addReturnURL(new URLHelper(commentsForm.getReferrer())); + _successUrl = new ActionURL(CopyParticipantCommentAction.class, container). + addParameter(ParticipantCommentForm.params.participantId, vial.getPtid()). + addParameter(ParticipantCommentForm.params.visitId, String.valueOf(vial.getVisitValue())). + addParameter(ParticipantCommentForm.params.comment, commentsForm.getComments()). + addReturnURL(new URLHelper(commentsForm.getReferrer())); } } else { - _successUrl = new ActionURL(urlProvider(SpecimenUrls.class).getCopyParticipantCommentActionClass(), container). - addParameter(ParticipantCommentForm.params.participantId, commentsForm.getCopyParticipantId()). - addParameter(ParticipantCommentForm.params.comment, commentsForm.getComments()). - addReturnURL(new URLHelper(commentsForm.getReferrer())); + _successUrl = new ActionURL(CopyParticipantCommentAction.class, container). + addParameter(ParticipantCommentForm.params.participantId, commentsForm.getCopyParticipantId()). + addParameter(ParticipantCommentForm.params.comment, commentsForm.getComments()). + addReturnURL(new URLHelper(commentsForm.getReferrer())); } // delete existing vial comments if move is specified @@ -5596,6 +5634,150 @@ public void addNavTrail(NavTree root) } } + @RequiresPermission(ReadPermission.class) + public class CopyParticipantCommentAction extends SimpleViewAction + { + @Override + public ModelAndView getView(final ParticipantCommentForm form, BindException errors) + { + Study study = getStudyRedirectIfNull(); + Dataset ds; + + if (form.getVisitId() != 0) + { + ds = StudyService.get().getDataset(study.getContainer(), StudyInternalService.get().getParticipantVisitCommentDatasetId(study)); + } + else + { + ds = StudyService.get().getDataset(study.getContainer(), StudyInternalService.get().getParticipantCommentDatasetId(study)); + } + + if (ds != null) + { + QueryView datasetQueryView = StudyInternalService.get().getDatasetQueryView(getViewContext(), study, getUser(), ds, form.getParticipantId(), form.getVisitId(), errors); + String lsid = getExistingComment(datasetQueryView); + ActionURL url; + if (lsid != null) + url = new ActionURL(ParticipantCommentAction.SpecimenCommentUpdateAction.class, getContainer()). + addParameter(ParticipantCommentForm.params.lsid, lsid); + else + url = new ActionURL(ParticipantCommentAction.SpecimenCommentInsertAction.class, getContainer()). + addParameter(ParticipantCommentForm.params.participantId, form.getParticipantId()); + + url.addParameter(ParticipantCommentForm.params.datasetId, ds.getDatasetId()). + addParameter(ParticipantCommentForm.params.comment, form.getComment()). + addReturnURL(form.getReturnActionURL()). + addParameter(ParticipantCommentForm.params.visitId, form.getVisitId()); + + for (int rowId : form.getVialCommentsToClear()) + url.addParameter(ParticipantCommentForm.params.vialCommentsToClear, rowId); + return HttpView.redirect(url); + } + return new HtmlView("Dataset could not be found"); + } + + @Override + public void addNavTrail(NavTree root) + { + } + + private String getExistingComment(QueryView queryView) + { + final TableInfo table = queryView.getTable(); + if (table != null) + { + try (ResultSet rs = queryView.getResultSet()) + { + while (rs.next()) + { + String lsid = rs.getString("lsid"); + if (lsid != null) + return lsid; + } + } + catch (Exception e) + { + LOG.error("Error encountered trying to get " + StudyService.get().getSubjectNounSingular(getContainer()) + " comments", e); + } + } + return null; + } + } + + @RequiresPermission(EditSpecimenDataPermission.class) + public static class UpdateSpecimenQueryRowAction extends UserSchemaAction + { + @Override + public ModelAndView getView(QueryUpdateForm tableForm, boolean reshow, BindException errors) + { + // Don't allow GlobalUniqueId to be edited + TableInfo tableInfo = tableForm.getTable(); + if (null != tableInfo.getColumn("GlobalUniqueId")) + ((BaseColumnInfo)tableInfo.getColumn("GlobalUniqueId")).setReadOnly(true); + + var vialComments = tableInfo.getColumn("VialComments"); + if (null != vialComments) + { + ((BaseColumnInfo)vialComments).setUserEditable(false); + ((BaseColumnInfo)vialComments).setHidden(true); + } + + StudyInternalService.get().fixSpecimenRequestableColumn(tableForm); + ButtonBar bb = createSubmitCancelButtonBar(tableForm); + UpdateView view = new UpdateView(tableForm, errors); + view.getDataRegion().setButtonBar(bb); + return view; + } + + @Override + public boolean handlePost(QueryUpdateForm tableForm, BindException errors) + { + doInsertUpdate(tableForm, errors, false); + return 0 == errors.getErrorCount(); + } + + @Override + public void addNavTrail(NavTree root) + { + super.addNavTrail(root); + root.addChild("Edit " + _form.getQueryName()); + } + } + + @RequiresPermission(EditSpecimenDataPermission.class) + public static class InsertSpecimenQueryRowAction extends UserSchemaAction + { + @Override + public ModelAndView getView(QueryUpdateForm tableForm, boolean reshow, BindException errors) + { + TableInfo tableInfo = tableForm.getTable(); + var vialComments = tableInfo.getColumn("VialComments"); + if (null != vialComments) + { + ((BaseColumnInfo)vialComments).setUserEditable(false); + ((BaseColumnInfo)vialComments).setHidden(true); + } + + StudyInternalService.get().fixSpecimenRequestableColumn(tableForm); + InsertView view = new InsertView(tableForm, errors); + view.getDataRegion().setButtonBar(createSubmitCancelButtonBar(tableForm)); + return view; + } + + @Override + public boolean handlePost(QueryUpdateForm tableForm, BindException errors) + { + doInsertUpdate(tableForm, errors, true); + return 0 == errors.getErrorCount(); + } + + @Override + public void addNavTrail(NavTree root) + { + super.addNavTrail(root); + root.addChild("Insert " + _form.getQueryName()); + } + } /* // Used for testing @RequiresSiteAdmin diff --git a/specimen/src/org/labkey/specimen/actions/UpdateSpecimenCommentsBean.java b/specimen/src/org/labkey/specimen/actions/UpdateSpecimenCommentsBean.java index a7ea119b..192ae0d5 100644 --- a/specimen/src/org/labkey/specimen/actions/UpdateSpecimenCommentsBean.java +++ b/specimen/src/org/labkey/specimen/actions/UpdateSpecimenCommentsBean.java @@ -4,7 +4,6 @@ import org.jetbrains.annotations.NotNull; import org.labkey.api.annotations.Migrate; import org.labkey.api.data.Container; -import org.labkey.api.specimen.SpecimenManager; import org.labkey.api.specimen.Vial; import org.labkey.api.specimen.model.SpecimenComment; import org.labkey.api.study.Study; @@ -13,10 +12,10 @@ import org.labkey.api.study.StudyUrls; import org.labkey.api.study.TimepointType; import org.labkey.api.study.Visit; -import org.labkey.api.study.model.VisitService; import org.labkey.api.util.PageFlowUtil; import org.labkey.api.view.RedirectException; import org.labkey.api.view.ViewContext; +import org.labkey.specimen.SpecimenManager; import java.util.Collections; import java.util.Date; diff --git a/specimen/src/org/labkey/specimen/report/participant/ParticipantSiteReportFactory.java b/specimen/src/org/labkey/specimen/report/participant/ParticipantSiteReportFactory.java index 6545c6d1..a5c03ef1 100644 --- a/specimen/src/org/labkey/specimen/report/participant/ParticipantSiteReportFactory.java +++ b/specimen/src/org/labkey/specimen/report/participant/ParticipantSiteReportFactory.java @@ -18,17 +18,17 @@ import org.labkey.api.data.SimpleFilter; import org.labkey.api.query.FieldKey; import org.labkey.api.security.User; -import org.labkey.api.specimen.SpecimenManager; import org.labkey.api.specimen.location.LocationImpl; import org.labkey.api.specimen.location.LocationManager; -import org.labkey.specimen.report.SpecimenVisitReport; -import org.labkey.specimen.report.SpecimenVisitReportAction; -import org.labkey.specimen.report.SpecimenVisitReportParameters; -import org.labkey.specimen.actions.SpecimenReportActions; import org.labkey.api.study.StudyService; import org.labkey.api.study.Visit; import org.labkey.api.util.HtmlString; import org.labkey.api.util.Pair; +import org.labkey.specimen.SpecimenManager; +import org.labkey.specimen.actions.SpecimenReportActions; +import org.labkey.specimen.report.SpecimenVisitReport; +import org.labkey.specimen.report.SpecimenVisitReportAction; +import org.labkey.specimen.report.SpecimenVisitReportParameters; import java.util.ArrayList; import java.util.Collections; diff --git a/specimen/src/org/labkey/specimen/report/participant/ParticipantSummaryReportFactory.java b/specimen/src/org/labkey/specimen/report/participant/ParticipantSummaryReportFactory.java index 7315d2fa..e4c3e31a 100644 --- a/specimen/src/org/labkey/specimen/report/participant/ParticipantSummaryReportFactory.java +++ b/specimen/src/org/labkey/specimen/report/participant/ParticipantSummaryReportFactory.java @@ -16,13 +16,13 @@ package org.labkey.specimen.report.participant; import org.labkey.api.data.SimpleFilter; -import org.labkey.api.specimen.SpecimenManager; +import org.labkey.api.study.StudyService; +import org.labkey.api.study.Visit; +import org.labkey.specimen.SpecimenManager; +import org.labkey.specimen.actions.SpecimenReportActions; import org.labkey.specimen.report.SpecimenVisitReport; import org.labkey.specimen.report.SpecimenVisitReportAction; import org.labkey.specimen.report.SpecimenVisitReportParameters; -import org.labkey.specimen.actions.SpecimenReportActions; -import org.labkey.api.study.StudyService; -import org.labkey.api.study.Visit; import java.util.Collections; import java.util.List; diff --git a/specimen/src/org/labkey/specimen/report/participant/ParticipantTypeReportFactory.java b/specimen/src/org/labkey/specimen/report/participant/ParticipantTypeReportFactory.java index d3170b07..ec8b7a5d 100644 --- a/specimen/src/org/labkey/specimen/report/participant/ParticipantTypeReportFactory.java +++ b/specimen/src/org/labkey/specimen/report/participant/ParticipantTypeReportFactory.java @@ -18,19 +18,19 @@ import org.labkey.api.data.CompareType; import org.labkey.api.data.SimpleFilter; import org.labkey.api.security.User; -import org.labkey.api.specimen.SpecimenManager; import org.labkey.api.specimen.SpecimenManagerNew; import org.labkey.api.specimen.model.SpecimenTypeSummary; -import org.labkey.specimen.report.SpecimenVisitReport; -import org.labkey.specimen.report.SpecimenVisitReportAction; -import org.labkey.specimen.report.SpecimenVisitReportParameters; -import org.labkey.specimen.actions.SpecimenReportActions; import org.labkey.api.study.StudyService; import org.labkey.api.study.Visit; import org.labkey.api.util.HtmlString; import org.labkey.api.util.Pair; import org.labkey.api.util.element.Option; import org.labkey.api.util.element.Select; +import org.labkey.specimen.SpecimenManager; +import org.labkey.specimen.actions.SpecimenReportActions; +import org.labkey.specimen.report.SpecimenVisitReport; +import org.labkey.specimen.report.SpecimenVisitReportAction; +import org.labkey.specimen.report.SpecimenVisitReportParameters; import java.util.ArrayList; import java.util.Collections; diff --git a/specimen/src/org/labkey/specimen/report/request/RequestEnrollmentLocationReport.java b/specimen/src/org/labkey/specimen/report/request/RequestEnrollmentLocationReport.java index 27836058..42f59458 100644 --- a/specimen/src/org/labkey/specimen/report/request/RequestEnrollmentLocationReport.java +++ b/specimen/src/org/labkey/specimen/report/request/RequestEnrollmentLocationReport.java @@ -16,11 +16,11 @@ package org.labkey.specimen.report.request; import org.labkey.api.data.SimpleFilter; -import org.labkey.specimen.report.SummaryByVisitType; -import org.labkey.api.study.Visit; import org.labkey.api.specimen.query.SpecimenQueryView; +import org.labkey.api.study.Visit; import org.labkey.specimen.report.SpecimenTypeVisitReport; import org.labkey.specimen.report.SpecimenVisitReportParameters; +import org.labkey.specimen.report.SummaryByVisitType; import java.util.List; diff --git a/specimen/src/org/labkey/specimen/report/request/RequestEnrollmentSiteReportFactory.java b/specimen/src/org/labkey/specimen/report/request/RequestEnrollmentSiteReportFactory.java index 299e8356..35d011a7 100644 --- a/specimen/src/org/labkey/specimen/report/request/RequestEnrollmentSiteReportFactory.java +++ b/specimen/src/org/labkey/specimen/report/request/RequestEnrollmentSiteReportFactory.java @@ -19,17 +19,17 @@ import org.labkey.api.data.SimpleFilter; import org.labkey.api.query.FieldKey; import org.labkey.api.security.User; -import org.labkey.api.specimen.SpecimenManager; import org.labkey.api.specimen.location.LocationImpl; import org.labkey.api.specimen.location.LocationManager; import org.labkey.api.specimen.query.SpecimenQueryView; -import org.labkey.specimen.report.SpecimenVisitReport; -import org.labkey.specimen.report.SpecimenVisitReportAction; -import org.labkey.specimen.actions.SpecimenReportActions; import org.labkey.api.study.Location; import org.labkey.api.study.Visit; import org.labkey.api.util.HtmlString; import org.labkey.api.util.Pair; +import org.labkey.specimen.SpecimenManager; +import org.labkey.specimen.actions.SpecimenReportActions; +import org.labkey.specimen.report.SpecimenVisitReport; +import org.labkey.specimen.report.SpecimenVisitReportAction; import java.util.ArrayList; import java.util.Collections; diff --git a/specimen/src/org/labkey/specimen/report/request/RequestLocationReport.java b/specimen/src/org/labkey/specimen/report/request/RequestLocationReport.java index 48918b48..db9284b5 100644 --- a/specimen/src/org/labkey/specimen/report/request/RequestLocationReport.java +++ b/specimen/src/org/labkey/specimen/report/request/RequestLocationReport.java @@ -16,11 +16,11 @@ package org.labkey.specimen.report.request; import org.labkey.api.data.SimpleFilter; -import org.labkey.specimen.report.SummaryByVisitType; -import org.labkey.api.study.Visit; import org.labkey.api.specimen.query.SpecimenQueryView; +import org.labkey.api.study.Visit; import org.labkey.specimen.report.SpecimenTypeVisitReport; import org.labkey.specimen.report.SpecimenVisitReportParameters; +import org.labkey.specimen.report.SummaryByVisitType; import java.util.List; diff --git a/specimen/src/org/labkey/specimen/report/request/RequestLocationReportFactory.java b/specimen/src/org/labkey/specimen/report/request/RequestLocationReportFactory.java index e28b022f..7a035388 100644 --- a/specimen/src/org/labkey/specimen/report/request/RequestLocationReportFactory.java +++ b/specimen/src/org/labkey/specimen/report/request/RequestLocationReportFactory.java @@ -17,18 +17,18 @@ import org.labkey.api.data.SimpleFilter; import org.labkey.api.security.User; -import org.labkey.api.specimen.SpecimenManager; import org.labkey.api.specimen.location.LocationImpl; import org.labkey.api.specimen.location.LocationManager; -import org.labkey.specimen.report.SpecimenVisitReport; -import org.labkey.specimen.report.SpecimenVisitReportAction; -import org.labkey.specimen.actions.SpecimenReportActions; import org.labkey.api.study.Location; import org.labkey.api.study.Visit; import org.labkey.api.util.HtmlString; import org.labkey.api.util.Pair; import org.labkey.api.util.element.Option; import org.labkey.api.util.element.Select; +import org.labkey.specimen.SpecimenManager; +import org.labkey.specimen.actions.SpecimenReportActions; +import org.labkey.specimen.report.SpecimenVisitReport; +import org.labkey.specimen.report.SpecimenVisitReportAction; import java.util.ArrayList; import java.util.Collections; diff --git a/specimen/src/org/labkey/specimen/report/request/RequestParticipantReportFactory.java b/specimen/src/org/labkey/specimen/report/request/RequestParticipantReportFactory.java index d5a016d7..e1169435 100644 --- a/specimen/src/org/labkey/specimen/report/request/RequestParticipantReportFactory.java +++ b/specimen/src/org/labkey/specimen/report/request/RequestParticipantReportFactory.java @@ -18,10 +18,6 @@ import org.labkey.api.data.SimpleFilter; import org.labkey.api.query.FieldKey; import org.labkey.api.security.User; -import org.labkey.api.specimen.SpecimenManager; -import org.labkey.specimen.report.SpecimenVisitReport; -import org.labkey.specimen.report.SpecimenVisitReportAction; -import org.labkey.specimen.actions.SpecimenReportActions; import org.labkey.api.study.Cohort; import org.labkey.api.study.Study; import org.labkey.api.study.StudyService; @@ -32,6 +28,10 @@ import org.labkey.api.util.DemoMode; import org.labkey.api.util.HtmlString; import org.labkey.api.util.Pair; +import org.labkey.specimen.SpecimenManager; +import org.labkey.specimen.actions.SpecimenReportActions; +import org.labkey.specimen.report.SpecimenVisitReport; +import org.labkey.specimen.report.SpecimenVisitReportAction; import java.util.ArrayList; import java.util.Collection; diff --git a/specimen/src/org/labkey/specimen/report/request/RequestReport.java b/specimen/src/org/labkey/specimen/report/request/RequestReport.java index 03fefa7e..9af593e9 100644 --- a/specimen/src/org/labkey/specimen/report/request/RequestReport.java +++ b/specimen/src/org/labkey/specimen/report/request/RequestReport.java @@ -16,11 +16,11 @@ package org.labkey.specimen.report.request; import org.labkey.api.data.SimpleFilter; -import org.labkey.specimen.report.SummaryByVisitType; -import org.labkey.api.study.Visit; import org.labkey.api.specimen.query.SpecimenQueryView; +import org.labkey.api.study.Visit; import org.labkey.specimen.report.SpecimenTypeVisitReport; import org.labkey.specimen.report.SpecimenVisitReportParameters; +import org.labkey.specimen.report.SummaryByVisitType; import java.util.List; diff --git a/specimen/src/org/labkey/specimen/report/request/RequestReportFactory.java b/specimen/src/org/labkey/specimen/report/request/RequestReportFactory.java index 95adc06f..99e8b29f 100644 --- a/specimen/src/org/labkey/specimen/report/request/RequestReportFactory.java +++ b/specimen/src/org/labkey/specimen/report/request/RequestReportFactory.java @@ -16,12 +16,12 @@ package org.labkey.specimen.report.request; import org.labkey.api.data.SimpleFilter; -import org.labkey.api.specimen.SpecimenManager; -import org.labkey.specimen.report.SpecimenVisitReport; -import org.labkey.specimen.report.SpecimenVisitReportAction; -import org.labkey.specimen.actions.SpecimenReportActions; import org.labkey.api.study.Location; import org.labkey.api.study.Visit; +import org.labkey.specimen.SpecimenManager; +import org.labkey.specimen.actions.SpecimenReportActions; +import org.labkey.specimen.report.SpecimenVisitReport; +import org.labkey.specimen.report.SpecimenVisitReportAction; import java.util.ArrayList; import java.util.Collections; diff --git a/specimen/src/org/labkey/specimen/report/specimentype/TypeCohortReportFactory.java b/specimen/src/org/labkey/specimen/report/specimentype/TypeCohortReportFactory.java index 49c50799..bc4ca6a1 100644 --- a/specimen/src/org/labkey/specimen/report/specimentype/TypeCohortReportFactory.java +++ b/specimen/src/org/labkey/specimen/report/specimentype/TypeCohortReportFactory.java @@ -17,10 +17,6 @@ import org.labkey.api.data.SimpleFilter; import org.labkey.api.security.User; -import org.labkey.api.specimen.SpecimenManager; -import org.labkey.specimen.report.SpecimenVisitReport; -import org.labkey.specimen.report.SpecimenVisitReportAction; -import org.labkey.specimen.actions.SpecimenReportActions; import org.labkey.api.study.Cohort; import org.labkey.api.study.CohortFilter; import org.labkey.api.study.Params; @@ -33,6 +29,10 @@ import org.labkey.api.util.element.Input; import org.labkey.api.util.element.Option; import org.labkey.api.util.element.Select; +import org.labkey.specimen.SpecimenManager; +import org.labkey.specimen.actions.SpecimenReportActions; +import org.labkey.specimen.report.SpecimenVisitReport; +import org.labkey.specimen.report.SpecimenVisitReportAction; import java.util.ArrayList; import java.util.List; diff --git a/specimen/src/org/labkey/specimen/report/specimentype/TypeParticipantReportFactory.java b/specimen/src/org/labkey/specimen/report/specimentype/TypeParticipantReportFactory.java index 19c55652..4753b229 100644 --- a/specimen/src/org/labkey/specimen/report/specimentype/TypeParticipantReportFactory.java +++ b/specimen/src/org/labkey/specimen/report/specimentype/TypeParticipantReportFactory.java @@ -18,11 +18,6 @@ import org.labkey.api.data.SimpleFilter; import org.labkey.api.query.FieldKey; import org.labkey.api.security.User; -import org.labkey.api.specimen.SpecimenManager; -import org.labkey.specimen.report.SpecimenTypeVisitReport; -import org.labkey.specimen.report.SpecimenVisitReport; -import org.labkey.specimen.report.SpecimenVisitReportAction; -import org.labkey.specimen.actions.SpecimenReportActions; import org.labkey.api.study.Cohort; import org.labkey.api.study.Study; import org.labkey.api.study.StudyService; @@ -33,6 +28,11 @@ import org.labkey.api.util.DemoMode; import org.labkey.api.util.HtmlString; import org.labkey.api.util.Pair; +import org.labkey.specimen.SpecimenManager; +import org.labkey.specimen.actions.SpecimenReportActions; +import org.labkey.specimen.report.SpecimenTypeVisitReport; +import org.labkey.specimen.report.SpecimenVisitReport; +import org.labkey.specimen.report.SpecimenVisitReportAction; import java.util.ArrayList; import java.util.Collection; diff --git a/specimen/src/org/labkey/specimen/report/specimentype/TypeReportFactory.java b/specimen/src/org/labkey/specimen/report/specimentype/TypeReportFactory.java index 556a9bc0..72a789ef 100644 --- a/specimen/src/org/labkey/specimen/report/specimentype/TypeReportFactory.java +++ b/specimen/src/org/labkey/specimen/report/specimentype/TypeReportFactory.java @@ -16,12 +16,12 @@ package org.labkey.specimen.report.specimentype; import org.labkey.api.security.User; -import org.labkey.specimen.report.SpecimenTypeLevel; import org.labkey.api.study.SpecimenService; import org.labkey.api.util.HtmlString; import org.labkey.api.util.Pair; import org.labkey.api.util.element.Option; import org.labkey.api.util.element.Select; +import org.labkey.specimen.report.SpecimenTypeLevel; import org.labkey.specimen.report.SpecimenVisitReportParameters; import java.util.ArrayList; diff --git a/specimen/src/org/labkey/specimen/report/specimentype/TypeSummaryReportFactory.java b/specimen/src/org/labkey/specimen/report/specimentype/TypeSummaryReportFactory.java index c965a3e3..96dd61aa 100644 --- a/specimen/src/org/labkey/specimen/report/specimentype/TypeSummaryReportFactory.java +++ b/specimen/src/org/labkey/specimen/report/specimentype/TypeSummaryReportFactory.java @@ -16,12 +16,12 @@ package org.labkey.specimen.report.specimentype; import org.labkey.api.data.SimpleFilter; -import org.labkey.api.specimen.SpecimenManager; +import org.labkey.api.study.Visit; +import org.labkey.specimen.SpecimenManager; +import org.labkey.specimen.actions.SpecimenReportActions; import org.labkey.specimen.report.SpecimenTypeVisitReport; import org.labkey.specimen.report.SpecimenVisitReport; import org.labkey.specimen.report.SpecimenVisitReportAction; -import org.labkey.specimen.actions.SpecimenReportActions; -import org.labkey.api.study.Visit; import java.util.Collections; import java.util.List; diff --git a/specimen/src/org/labkey/specimen/view/manageRequest.jsp b/specimen/src/org/labkey/specimen/view/manageRequest.jsp index dbfeef87..c074863d 100644 --- a/specimen/src/org/labkey/specimen/view/manageRequest.jsp +++ b/specimen/src/org/labkey/specimen/view/manageRequest.jsp @@ -21,7 +21,6 @@ <%@ page import="org.labkey.api.security.User"%> <%@ page import="org.labkey.api.security.UserManager"%> <%@ page import="org.labkey.api.settings.AppProps"%> -<%@ page import="org.labkey.api.specimen.SpecimenManager" %> <%@ page import="org.labkey.api.specimen.SpecimenRequestManager" %> <%@ page import="org.labkey.api.specimen.SpecimenRequestStatus"%> <%@ page import="org.labkey.api.specimen.Vial" %> @@ -40,6 +39,7 @@ <%@ 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.specimen.SpecimenManager" %> <%@ page import="org.labkey.specimen.actions.ManageRequestBean" %> <%@ page import="org.labkey.specimen.actions.ShowSearchAction" %> <%@ page import="org.labkey.specimen.actions.SpecimenController" %> diff --git a/specimen/src/org/labkey/specimen/view/requestSpecimens.jsp b/specimen/src/org/labkey/specimen/view/requestSpecimens.jsp index 8a4f624f..23cf03e4 100644 --- a/specimen/src/org/labkey/specimen/view/requestSpecimens.jsp +++ b/specimen/src/org/labkey/specimen/view/requestSpecimens.jsp @@ -16,7 +16,6 @@ */ %> <%@ page import="org.labkey.api.data.Container"%> -<%@ page import="org.labkey.api.specimen.SpecimenManager"%> <%@ page import="org.labkey.api.specimen.SpecimenRequestManager.SpecimenRequestInput"%> <%@ page import="org.labkey.api.specimen.Vial"%> <%@ page import="org.labkey.api.specimen.location.LocationImpl"%> @@ -28,6 +27,7 @@ <%@ page import="org.labkey.api.view.JspView" %> <%@ page import="org.labkey.api.view.ViewContext" %> <%@ page import="org.labkey.api.view.template.ClientDependencies" %> +<%@ page import="org.labkey.specimen.SpecimenManager" %> <%@ page import="org.labkey.specimen.actions.SpecimenController.CreateSpecimenRequestForm" %> <%@ page import="org.labkey.specimen.actions.SpecimenController.HandleCreateSpecimenRequestAction" %> <%@ page import="org.labkey.specimen.actions.SpecimenController.NewRequestBean" %> diff --git a/specimen/src/org/labkey/specimen/view/specimen.jsp b/specimen/src/org/labkey/specimen/view/specimen.jsp index 72e17406..7010cf95 100644 --- a/specimen/src/org/labkey/specimen/view/specimen.jsp +++ b/specimen/src/org/labkey/specimen/view/specimen.jsp @@ -15,7 +15,6 @@ * limitations under the License. */ %> -<%@ page import="org.labkey.api.specimen.SpecimenManager"%> <%@ page import="org.labkey.api.specimen.Vial"%> <%@ page import="org.labkey.api.specimen.location.LocationManager"%> <%@ page import="org.labkey.api.specimen.model.SpecimenComment" %> @@ -24,6 +23,7 @@ <%@ 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.specimen.SpecimenManager" %> <%@ page import="org.labkey.specimen.actions.SpecimenController" %> <%@ page import="org.labkey.specimen.actions.SpecimenEventBean" %> <%@ page import="org.labkey.specimen.security.permissions.SetSpecimenCommentsPermission" %> From efebf823363c9c7a8ceeaaf111662f5fbfad4678 Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Wed, 21 Jul 2021 12:32:22 -0700 Subject: [PATCH 344/494] Finish double -> BigDecimal sequence num migration Original PR: https://github.com/LabKey/platform/pull/2468 --- .../labkey/specimen/actions/UpdateSpecimenCommentsBean.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/specimen/src/org/labkey/specimen/actions/UpdateSpecimenCommentsBean.java b/specimen/src/org/labkey/specimen/actions/UpdateSpecimenCommentsBean.java index 192ae0d5..fe432365 100644 --- a/specimen/src/org/labkey/specimen/actions/UpdateSpecimenCommentsBean.java +++ b/specimen/src/org/labkey/specimen/actions/UpdateSpecimenCommentsBean.java @@ -17,6 +17,7 @@ import org.labkey.api.view.ViewContext; import org.labkey.specimen.SpecimenManager; +import java.math.BigDecimal; import java.util.Collections; import java.util.Date; import java.util.HashMap; @@ -102,7 +103,8 @@ protected Map> generateParticipantVisitMap(List if (visit != null) { String ptid = vial.getPtid(); - Visit v = StudyInternalService.get().getVisitForSequence(study, visit); + BigDecimal sequenceNum = StudyInternalService.get().getSequenceNum(visit); + Visit v = StudyInternalService.get().getVisitForSequence(study, sequenceNum); if (ptid != null && v != null) { if (!pvMap.containsKey(ptid)) From 69f9d18c960bba73f234a2319619390ce5bbd861 Mon Sep 17 00:00:00 2001 From: labkey-sweta Date: Mon, 26 Jul 2021 09:23:11 -0700 Subject: [PATCH 345/494] Changing the Category from DailyA, DailyB and DailyC to Daily to be inline with new teamcity configuration of daily suits Original PR: https://github.com/LabKey/platform/pull/2461 --- .../src/org/labkey/test/tests/study/SpecimenMergeTest.java | 4 ++-- .../src/org/labkey/test/tests/study/SpecimenReplaceTest.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/study/test/src/org/labkey/test/tests/study/SpecimenMergeTest.java b/study/test/src/org/labkey/test/tests/study/SpecimenMergeTest.java index c4fa5b20..a1373f23 100644 --- a/study/test/src/org/labkey/test/tests/study/SpecimenMergeTest.java +++ b/study/test/src/org/labkey/test/tests/study/SpecimenMergeTest.java @@ -21,7 +21,7 @@ import org.labkey.test.Locator; import org.labkey.test.TestFileUtils; import org.labkey.test.TestTimeoutException; -import org.labkey.test.categories.DailyB; +import org.labkey.test.categories.Daily; import org.labkey.test.categories.Specimen; import org.labkey.test.util.StudyHelper; @@ -32,7 +32,7 @@ /** * CreateVialsTest also uses the specimen merge feature. */ -@Category({DailyB.class, Specimen.class}) +@Category({Daily.class, Specimen.class}) @BaseWebDriverTest.ClassTimeout(minutes = 7) public class SpecimenMergeTest extends BaseWebDriverTest { diff --git a/study/test/src/org/labkey/test/tests/study/SpecimenReplaceTest.java b/study/test/src/org/labkey/test/tests/study/SpecimenReplaceTest.java index 7de95c34..da0441a0 100644 --- a/study/test/src/org/labkey/test/tests/study/SpecimenReplaceTest.java +++ b/study/test/src/org/labkey/test/tests/study/SpecimenReplaceTest.java @@ -20,12 +20,12 @@ import org.labkey.test.BaseWebDriverTest; import org.labkey.test.Locator; import org.labkey.test.TestFileUtils; -import org.labkey.test.categories.DailyA; +import org.labkey.test.categories.Daily; import org.labkey.test.categories.Specimen; import java.io.File; -@Category({DailyA.class, Specimen.class}) +@Category({Daily.class, Specimen.class}) @BaseWebDriverTest.ClassTimeout(minutes = 6) public class SpecimenReplaceTest extends SpecimenMergeTest { From 98585d77eec1c1b6154ac66bc87dcda3faaeef1b Mon Sep 17 00:00:00 2001 From: labkey-sweta Date: Mon, 26 Jul 2021 11:33:06 -0700 Subject: [PATCH 346/494] Changing the test class category. Original PR: https://github.com/LabKey/testAutomation/pull/802 * Changing the Category from DailyA, DailyB and DailyC to Daily to be inline with new teamcity configuration of daily suits * Code review change. --- src/org/labkey/test/tests/AliquotTest.java | 4 ++-- src/org/labkey/test/tests/CustomizeEmailTemplateTest.java | 4 ++-- src/org/labkey/test/tests/SpecimenCustomizeTest.java | 4 ++-- src/org/labkey/test/tests/SpecimenExportTest.java | 4 ++-- src/org/labkey/test/tests/SpecimenGridExportTest.java | 4 ++-- src/org/labkey/test/tests/SpecimenImportTest.java | 4 ++-- src/org/labkey/test/tests/SpecimenMultipleImportTest.java | 4 ++-- src/org/labkey/test/tests/SpecimenTest.java | 4 ++-- 8 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/org/labkey/test/tests/AliquotTest.java b/src/org/labkey/test/tests/AliquotTest.java index ced6b047..a601f8df 100644 --- a/src/org/labkey/test/tests/AliquotTest.java +++ b/src/org/labkey/test/tests/AliquotTest.java @@ -22,7 +22,7 @@ import org.labkey.test.Locators; import org.labkey.test.TestFileUtils; import org.labkey.test.TestTimeoutException; -import org.labkey.test.categories.DailyC; +import org.labkey.test.categories.Daily; import org.labkey.test.categories.Specimen; import org.labkey.test.components.html.BootstrapMenu; import org.labkey.test.util.DataRegionTable; @@ -31,7 +31,7 @@ import java.io.File; -@Category({DailyC.class, Specimen.class}) +@Category({Daily.class, Specimen.class}) @BaseWebDriverTest.ClassTimeout(minutes = 8) public class AliquotTest extends SpecimenBaseTest { diff --git a/src/org/labkey/test/tests/CustomizeEmailTemplateTest.java b/src/org/labkey/test/tests/CustomizeEmailTemplateTest.java index b0f3552f..2347e168 100644 --- a/src/org/labkey/test/tests/CustomizeEmailTemplateTest.java +++ b/src/org/labkey/test/tests/CustomizeEmailTemplateTest.java @@ -21,7 +21,7 @@ import org.labkey.test.Locator; import org.labkey.test.Locators; import org.labkey.test.WebTestHelper; -import org.labkey.test.categories.DailyC; +import org.labkey.test.categories.Daily; import org.labkey.test.components.dumbster.EmailRecordTable; import org.labkey.test.util.DataRegionTable; import org.labkey.test.util.LogMethod; @@ -35,7 +35,7 @@ import static org.junit.Assert.assertEquals; -@Category(DailyC.class) +@Category(Daily.class) @BaseWebDriverTest.ClassTimeout(minutes = 5) public class CustomizeEmailTemplateTest extends SpecimenBaseTest { diff --git a/src/org/labkey/test/tests/SpecimenCustomizeTest.java b/src/org/labkey/test/tests/SpecimenCustomizeTest.java index cc0c30cd..a2243270 100644 --- a/src/org/labkey/test/tests/SpecimenCustomizeTest.java +++ b/src/org/labkey/test/tests/SpecimenCustomizeTest.java @@ -20,7 +20,7 @@ import org.labkey.test.BaseWebDriverTest; import org.labkey.test.Locator; import org.labkey.test.TestFileUtils; -import org.labkey.test.categories.DailyC; +import org.labkey.test.categories.Daily; import org.labkey.test.categories.Specimen; import org.labkey.test.components.CustomizeView; import org.labkey.test.components.DomainDesignerPage; @@ -38,7 +38,7 @@ import java.util.Arrays; import java.util.List; -@Category({DailyC.class, Specimen.class}) +@Category({Daily.class, Specimen.class}) @BaseWebDriverTest.ClassTimeout(minutes = 7) public class SpecimenCustomizeTest extends SpecimenBaseTest { diff --git a/src/org/labkey/test/tests/SpecimenExportTest.java b/src/org/labkey/test/tests/SpecimenExportTest.java index fee4bfba..f2aaac38 100644 --- a/src/org/labkey/test/tests/SpecimenExportTest.java +++ b/src/org/labkey/test/tests/SpecimenExportTest.java @@ -20,7 +20,7 @@ import org.labkey.test.Locator; import org.labkey.test.TestFileUtils; import org.labkey.test.TestTimeoutException; -import org.labkey.test.categories.DailyC; +import org.labkey.test.categories.Daily; import org.labkey.test.categories.Specimen; import org.labkey.test.util.LogMethod; import org.labkey.test.util.StudyHelper; @@ -29,7 +29,7 @@ import static org.junit.Assert.assertTrue; -@Category({DailyC.class, Specimen.class}) +@Category({Daily.class, Specimen.class}) @BaseWebDriverTest.ClassTimeout(minutes = 6) public class SpecimenExportTest extends SpecimenBaseTest { diff --git a/src/org/labkey/test/tests/SpecimenGridExportTest.java b/src/org/labkey/test/tests/SpecimenGridExportTest.java index 15c75a61..ea6ce6ba 100644 --- a/src/org/labkey/test/tests/SpecimenGridExportTest.java +++ b/src/org/labkey/test/tests/SpecimenGridExportTest.java @@ -21,7 +21,7 @@ import org.labkey.test.BaseWebDriverTest; import org.labkey.test.Locator; import org.labkey.test.TestTimeoutException; -import org.labkey.test.categories.DailyB; +import org.labkey.test.categories.Daily; import org.labkey.test.categories.Specimen; import org.labkey.test.util.AbstractDataRegionExportOrSignHelper.ColumnHeaderType; @@ -31,7 +31,7 @@ /** * Test exporting rows from a specimen grid (not folder/study specimen export.) */ -@Category({DailyB.class, Specimen.class}) +@Category({Daily.class, Specimen.class}) @BaseWebDriverTest.ClassTimeout(minutes = 7) public class SpecimenGridExportTest extends AbstractExportTest { diff --git a/src/org/labkey/test/tests/SpecimenImportTest.java b/src/org/labkey/test/tests/SpecimenImportTest.java index 3af96ac2..fd65111d 100644 --- a/src/org/labkey/test/tests/SpecimenImportTest.java +++ b/src/org/labkey/test/tests/SpecimenImportTest.java @@ -26,7 +26,7 @@ import org.labkey.test.BaseWebDriverTest; import org.labkey.test.Locator; import org.labkey.test.WebTestHelper; -import org.labkey.test.categories.DailyC; +import org.labkey.test.categories.Daily; import org.labkey.test.categories.Specimen; import org.labkey.test.components.ext4.RadioButton; import org.labkey.test.util.DataRegionTable; @@ -42,7 +42,7 @@ import static org.junit.Assert.assertTrue; -@Category({DailyC.class, Specimen.class}) +@Category({Daily.class, Specimen.class}) @BaseWebDriverTest.ClassTimeout(minutes = 5) public class SpecimenImportTest extends SpecimenBaseTest { diff --git a/src/org/labkey/test/tests/SpecimenMultipleImportTest.java b/src/org/labkey/test/tests/SpecimenMultipleImportTest.java index b0177280..ab4a3c0d 100644 --- a/src/org/labkey/test/tests/SpecimenMultipleImportTest.java +++ b/src/org/labkey/test/tests/SpecimenMultipleImportTest.java @@ -20,14 +20,14 @@ import org.labkey.test.BaseWebDriverTest; import org.labkey.test.Locator; import org.labkey.test.TestFileUtils; -import org.labkey.test.categories.DailyC; +import org.labkey.test.categories.Daily; import org.labkey.test.categories.Specimen; import org.labkey.test.util.LogMethod; import org.labkey.test.util.StudyHelper; import java.io.File; -@Category({DailyC.class, Specimen.class}) +@Category({Daily.class, Specimen.class}) @BaseWebDriverTest.ClassTimeout(minutes = 6) public class SpecimenMultipleImportTest extends StudyBaseTest { diff --git a/src/org/labkey/test/tests/SpecimenTest.java b/src/org/labkey/test/tests/SpecimenTest.java index 57753e58..45e6bdd7 100644 --- a/src/org/labkey/test/tests/SpecimenTest.java +++ b/src/org/labkey/test/tests/SpecimenTest.java @@ -26,7 +26,7 @@ import org.labkey.test.TestFileUtils; import org.labkey.test.TestTimeoutException; import org.labkey.test.WebTestHelper; -import org.labkey.test.categories.DailyC; +import org.labkey.test.categories.Daily; import org.labkey.test.categories.Specimen; import org.labkey.test.components.CustomizeView; import org.labkey.test.components.dumbster.EmailRecordTable; @@ -60,7 +60,7 @@ import static org.labkey.test.pages.study.specimen.ManageNotificationsPage.SpecimensAttachment; import static org.labkey.test.util.DataRegionTable.DataRegion; -@Category({DailyC.class, Specimen.class}) +@Category({Daily.class, Specimen.class}) @BaseWebDriverTest.ClassTimeout(minutes = 20) public class SpecimenTest extends SpecimenBaseTest { From 57f9cbc3044193a0d7aea92ef0a0ba84d40caa4c Mon Sep 17 00:00:00 2001 From: Ian Sigmon Date: Wed, 8 Sep 2021 12:35:07 -0700 Subject: [PATCH 347/494] Enable import for Folder/Study archives when paired with the cloud module Original PR: https://github.com/LabKey/platform/pull/2541 Adjusted pipeline tasks and jobs to work with the cloud modules - Refactored many usages of io.File to nio.Path - Added/Adjusted some APIs to return nio.Paths - deprecated some File based apis in favor of new Path based versions Co-authored-by: labkey-tchad --- .../pipeline/FileAnalysisSpecimenTask.java | 21 ++--- .../pipeline/SampleMindedTransform.java | 9 +++ .../pipeline/SampleMindedTransformTask.java | 80 ++++++++++--------- .../specimen/pipeline/SpecimenBatch.java | 9 ++- 4 files changed, 71 insertions(+), 48 deletions(-) diff --git a/specimen/src/org/labkey/specimen/pipeline/FileAnalysisSpecimenTask.java b/specimen/src/org/labkey/specimen/pipeline/FileAnalysisSpecimenTask.java index 73f43a66..cffad094 100644 --- a/specimen/src/org/labkey/specimen/pipeline/FileAnalysisSpecimenTask.java +++ b/specimen/src/org/labkey/specimen/pipeline/FileAnalysisSpecimenTask.java @@ -22,7 +22,10 @@ import java.io.File; import java.io.FileInputStream; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.Date; +import java.util.List; // Registered by the specimen module (specimenContext.xml), so specimen module is always present when this code is invoked. public class FileAnalysisSpecimenTask extends AbstractSpecimenTask @@ -35,13 +38,13 @@ public FileAnalysisSpecimenTask(FileAnalysisSpecimenTask.Factory factory, Pipeli } @Override - protected File getSpecimenFile(PipelineJob job) + protected Path getSpecimenFile(PipelineJob job) { FileAnalysisJobSupport support = job.getJobSupport(FileAnalysisJobSupport.class); - + List paths = support.getInputFilePaths(); // there should only be a single file associated with this task - assert support.getInputFiles().size() == 1; - return support.getInputFiles().get(0); + assert paths.size() == 1; + return paths.get(0); } @Override @@ -71,14 +74,14 @@ public AbstractSpecimenTask.ImportHelper getImportHelper() private static class ImportHelper extends DefaultImportHelper { - private File _tempDir; + private Path _tempDir; @Override - public VirtualFile getSpecimenDir(PipelineJob job, SimpleStudyImportContext ctx, @Nullable File inputFile) throws IOException, ImportException, PipelineJobException + public VirtualFile getSpecimenDir(PipelineJob job, SimpleStudyImportContext ctx, @Nullable Path inputFile) throws IOException, ImportException, PipelineJobException { if (inputFile != null) { - try (TikaInputStream is = TikaInputStream.get(new FileInputStream(inputFile))) + try (TikaInputStream is = TikaInputStream.get(Files.newInputStream(inputFile))) { // determine the type of file being imported DefaultDetector detector = new DefaultDetector(); @@ -96,8 +99,8 @@ else if (MediaType.TEXT_PLAIN.equals(type)) // ctx.getLogger().info("Single specimen file detected, moving to a temp folder for processing."); String tempDirName = DateUtil.formatDateTime(new Date(), "yyMMddHHmmssSSS"); - _tempDir = new File(inputFile.getParentFile(), tempDirName); - FileUtils.copyToDirectory(inputFile, _tempDir); + _tempDir = inputFile.getParent().resolve(tempDirName); + Files.copy(inputFile, _tempDir); return new FileSystemFile(_tempDir); } diff --git a/specimen/src/org/labkey/specimen/pipeline/SampleMindedTransform.java b/specimen/src/org/labkey/specimen/pipeline/SampleMindedTransform.java index 26f2e826..850fc2a8 100644 --- a/specimen/src/org/labkey/specimen/pipeline/SampleMindedTransform.java +++ b/specimen/src/org/labkey/specimen/pipeline/SampleMindedTransform.java @@ -27,6 +27,7 @@ import org.labkey.api.view.ActionURL; import java.io.File; +import java.nio.file.Path; /** * User: klum @@ -59,7 +60,15 @@ public FileType getFileType() } @Override + @Deprecated //Prefer the Path version public void transform(@Nullable PipelineJob job, File input, File outputArchive) throws PipelineJobException + { + //This should be safe since the File --> Path conversion is fairly safe + transform(job, input.toPath(), outputArchive.toPath()); + } + + @Override + public void transform(@Nullable PipelineJob job, Path input, Path outputArchive) throws PipelineJobException { SampleMindedTransformTask task = new SampleMindedTransformTask(job); task.transform(input, outputArchive); diff --git a/specimen/src/org/labkey/specimen/pipeline/SampleMindedTransformTask.java b/specimen/src/org/labkey/specimen/pipeline/SampleMindedTransformTask.java index d816e18d..a15bf4dd 100644 --- a/specimen/src/org/labkey/specimen/pipeline/SampleMindedTransformTask.java +++ b/specimen/src/org/labkey/specimen/pipeline/SampleMindedTransformTask.java @@ -71,11 +71,12 @@ import java.io.BufferedReader; import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; import java.io.IOException; +import java.io.InputStream; import java.io.PrintWriter; import java.io.StringReader; +import java.nio.file.Files; +import java.nio.file.Path; import java.sql.SQLException; import java.util.ArrayList; import java.util.Collections; @@ -197,16 +198,16 @@ protected Set getIgnoredHashColumns() } @Override - public void transform(File input, File output) throws PipelineJobException + public void transform(Path input, Path output) throws PipelineJobException { info("Starting to transform input file " + input + " to output file " + output); try { - File labsFile = new File(input.getParent(), "labs.txt"); - if (NetworkDrive.exists(labsFile) && labsFile.isFile()) + Path labsFile = input.getParent().resolve("labs.txt"); + if (NetworkDrive.exists(labsFile) && Files.isRegularFile(labsFile)) { - try (FileInputStream fIn = new FileInputStream(labsFile)) + try (InputStream fIn = Files.newInputStream(labsFile)) { parseLabs(_labIds, Readers.getReader(fIn)); } @@ -217,42 +218,45 @@ public void transform(File input, File output) throws PipelineJobException debug("No such file " + labsFile + " so not parsing supplemental lab information"); } - ExcelLoader loader = new ExcelLoader(input, true); - loader.setInferTypes(false); - - // Create a ZIP archive with the appropriate TSVs - try (ZipOutputStream zOut = new ZipOutputStream(new FileOutputStream(output))) + try (InputStream is = Files.newInputStream(input)) { - // Add a new file to the ZIP - zOut.putNextEntry(new ZipEntry("specimens.tsv")); - PrintWriter writer = PrintWriters.getPrintWriter(zOut); - final SpecimenTransformTSVWriter tsvWriter = new SpecimenTransformTSVWriter(Collections.emptyList()); - tsvWriter.setFileHeader(Collections.singletonList("# " + "specimens")); - tsvWriter.setPrintWriter(writer); - try - { - loader.forEach(row -> { - if (MapFilter.getMapFilter(this).test(row)) - { - tsvWriter.writeRow(MapTransformer.getMapTransformer(this, getLabIds(), getPrimaryIds(), getDerivativeIds()).apply(row)); - } - }); - } - finally + ExcelLoader loader = new ExcelLoader(is, true, null); + loader.setInferTypes(false); + + // Create a ZIP archive with the appropriate TSVs + try (ZipOutputStream zOut = new ZipOutputStream(Files.newOutputStream(output))) { - writer.flush(); - zOut.closeEntry(); - } + // Add a new file to the ZIP + zOut.putNextEntry(new ZipEntry("specimens.tsv")); + PrintWriter writer = PrintWriters.getPrintWriter(zOut); + final SpecimenTransformTSVWriter tsvWriter = new SpecimenTransformTSVWriter(Collections.emptyList()); + tsvWriter.setFileHeader(Collections.singletonList("# " + "specimens")); + tsvWriter.setPrintWriter(writer); + try + { + loader.forEach(row -> { + if (MapFilter.getMapFilter(this).test(row)) + { + tsvWriter.writeRow(MapTransformer.getMapTransformer(this, getLabIds(), getPrimaryIds(), getDerivativeIds()).apply(row)); + } + }); + } + finally + { + writer.flush(); + zOut.closeEntry(); + } - if (tsvWriter.getRowCount() > 0) - info("After removing duplicates, there are " + tsvWriter.getRowCount() + " rows of data"); - else - throw new PipelineJobException("There are no rows of data"); + if (tsvWriter.getRowCount() > 0) + info("After removing duplicates, there are " + tsvWriter.getRowCount() + " rows of data"); + else + throw new PipelineJobException("There are no rows of data"); - writeLabs(_labIds, zOut); - writePrimaries(_primaryIds, zOut); - writeDerivatives(_derivativeIds, zOut); - writeAdditives(Collections.emptyMap(), zOut); + writeLabs(_labIds, zOut); + writePrimaries(_primaryIds, zOut); + writeDerivatives(_derivativeIds, zOut); + writeAdditives(Collections.emptyMap(), zOut); + } } } catch (IOException e) diff --git a/specimen/src/org/labkey/specimen/pipeline/SpecimenBatch.java b/specimen/src/org/labkey/specimen/pipeline/SpecimenBatch.java index 4fe5dcdf..131b932c 100644 --- a/specimen/src/org/labkey/specimen/pipeline/SpecimenBatch.java +++ b/specimen/src/org/labkey/specimen/pipeline/SpecimenBatch.java @@ -33,6 +33,7 @@ import java.io.File; import java.io.Serializable; +import java.nio.file.Path; /** * User: brittp @@ -81,6 +82,12 @@ public File getSpecimenArchive() return _definitionFile; } + @Override + public Path getSpecimenArchivePath() + { + return _definitionFile.toPath(); + } + @Override public boolean isMerge() { @@ -98,4 +105,4 @@ public TaskPipeline getTaskPipeline() { return PipelineJobService.get().getTaskPipeline(new TaskId(SpecimenBatch.class)); } -} \ No newline at end of file +} From 5ae66a0fd4c7bfbb42be44dc2d68ff99edc9e9a0 Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Mon, 18 Oct 2021 16:35:38 -0700 Subject: [PATCH 348/494] Simplify HelpTopic declarations Original PR: https://github.com/LabKey/platform/pull/2695 --- .../specimen/actions/SpecimenController.java | 43 ++++--------------- 1 file changed, 8 insertions(+), 35 deletions(-) diff --git a/specimen/src/org/labkey/specimen/actions/SpecimenController.java b/specimen/src/org/labkey/specimen/actions/SpecimenController.java index b499cff6..d411b420 100644 --- a/specimen/src/org/labkey/specimen/actions/SpecimenController.java +++ b/specimen/src/org/labkey/specimen/actions/SpecimenController.java @@ -109,7 +109,6 @@ import org.labkey.api.util.ExceptionUtil; import org.labkey.api.util.FileStream; import org.labkey.api.util.GUID; -import org.labkey.api.util.HelpTopic; import org.labkey.api.util.Link; import org.labkey.api.util.MailHelper; import org.labkey.api.util.PageFlowUtil; @@ -1566,7 +1565,7 @@ public ActionURL getSuccessURL(DisplaySettingsForm displaySettingsForm) @Override public void addNavTrail(NavTree root) { - setHelpTopic(new HelpTopic("specimenRequest#display")); + setHelpTopic("specimenRequest#display"); addManageStudyNavTrail(root); root.addChild("Manage Specimen Display Settings"); } @@ -2728,15 +2727,13 @@ public void validateCommand(Form target, Errors errors) public ModelAndView getView(Form form, boolean reshow, BindException errors) { ensureSpecimenRequestsConfigured(false); - return getJspView(getStudyRedirectIfNull()); + return new JspView<>(_jsp, getStudyRedirectIfNull()); } - protected abstract JspView getJspView(Study study); - @Override public void addNavTrail(NavTree root) { - setHelpTopic(new HelpTopic(_helpTopic)); + setHelpTopic(_helpTopic); addManageStudyNavTrail(root); root.addChild(_title); } @@ -2772,13 +2769,7 @@ public class ManageActorsAction extends DisplayManagementSubpageAction getJspView(Study study) - { - return new JspView<>("/org/labkey/specimen/view/manageActors.jsp", study); + super("/org/labkey/specimen/view/manageActors.jsp", "Manage Specimen Request Actors", "coordinateSpecimens#actor"); } @Override @@ -2846,13 +2837,7 @@ public class ManageActorOrderAction extends DisplayManagementSubpageAction getJspView(Study study) - { - return new JspView<>("/org/labkey/specimen/view/manageActorOrder.jsp", study); + super("/org/labkey/specimen/view/manageActorOrder.jsp", "Manage Actor Order", "specimenRequest"); } @Override @@ -3006,13 +2991,7 @@ public class ManageStatusesAction extends DisplayManagementSubpageAction getJspView(Study study) - { - return new JspView<>("/org/labkey/specimen/view/manageStatuses.jsp", study); + super("/org/labkey/specimen/view/manageStatuses.jsp", "Manage Specimen Request Statuses", "specimenRequest#status"); } @Override @@ -3093,13 +3072,7 @@ public class ManageStatusOrderAction extends DisplayManagementSubpageAction getJspView(Study study) - { - return new JspView<>("/org/labkey/specimen/view/manageStatusOrder.jsp", study); + super("/org/labkey/specimen/view/manageStatusOrder.jsp", "Manage Status Order", "specimenRequest"); } @Override @@ -5308,7 +5281,7 @@ public void validateCommand(EnabledSpecimenImportForm target, Errors errors) @Override public ModelAndView getView(EnabledSpecimenImportForm form, boolean reshow, BindException errors) throws Exception { - setHelpTopic(new HelpTopic("externalSpecimens")); + setHelpTopic("externalSpecimens"); return new JspView<>("/org/labkey/specimen/view/chooseImporter.jsp", form, errors); } From a4dfe9093d4948ca8a20c1ec88036e1d9c998bc6 Mon Sep 17 00:00:00 2001 From: labkey-matthewb Date: Fri, 19 Nov 2021 14:47:52 -0800 Subject: [PATCH 349/494] remove Dataset.canInsert()/canUpdate()/canDelete() Original PR: https://github.com/LabKey/platform/pull/2790 --- .../org/labkey/specimen/actions/SpecimenController.java | 6 ++++-- specimen/src/org/labkey/specimen/view/updateComments.jsp | 8 ++++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/specimen/src/org/labkey/specimen/actions/SpecimenController.java b/specimen/src/org/labkey/specimen/actions/SpecimenController.java index d411b420..a0d641de 100644 --- a/specimen/src/org/labkey/specimen/actions/SpecimenController.java +++ b/specimen/src/org/labkey/specimen/actions/SpecimenController.java @@ -52,6 +52,7 @@ import org.labkey.api.security.ValidEmail; import org.labkey.api.security.permissions.AdminPermission; import org.labkey.api.security.permissions.ReadPermission; +import org.labkey.api.security.permissions.UpdatePermission; import org.labkey.api.specimen.AmbiguousLocationException; import org.labkey.api.specimen.RequestEventType; import org.labkey.api.specimen.RequestedSpecimens; @@ -459,7 +460,7 @@ public SpecimenQueryView getSpecimenQueryView(boolean showVials, boolean forExpo if (participantCommentDatasetId != null && participantCommentDatasetId != -1) { Dataset ds = StudyService.get().getDataset(study.getContainer(), participantCommentDatasetId); - if (ds != null && ds.canUpdate(getUser())) + if (ds != null && ds.getTableInfo(getUser()).hasPermission(getUser(), UpdatePermission.class)) { if (addSep) { @@ -477,7 +478,8 @@ public SpecimenQueryView getSpecimenQueryView(boolean showVials, boolean forExpo if (participantVisitCommentDatasetId != null && participantVisitCommentDatasetId != -1) { Dataset ds = StudyService.get().getDataset(study.getContainer(), participantVisitCommentDatasetId); - if (ds != null && ds.canUpdate(getUser())) + TableInfo table = null==ds ? null : ds.getTableInfo(getUser()); + if (null != table && table.hasPermission(getUser(), UpdatePermission.class)) { if (addSep) { diff --git a/specimen/src/org/labkey/specimen/view/updateComments.jsp b/specimen/src/org/labkey/specimen/view/updateComments.jsp index 16e23a19..6cf1cd2b 100644 --- a/specimen/src/org/labkey/specimen/view/updateComments.jsp +++ b/specimen/src/org/labkey/specimen/view/updateComments.jsp @@ -34,6 +34,8 @@ <%@ page import="org.labkey.specimen.actions.SpecimenController.UpdateCommentsAction" %> <%@ page import="org.labkey.specimen.actions.UpdateSpecimenCommentsBean" %> <%@ page import="java.util.Map" %> +<%@ page import="org.labkey.api.data.TableInfo" %> +<%@ page import="org.labkey.api.security.permissions.UpdatePermission" %> <%@ taglib prefix="labkey" uri="http://www.labkey.org/taglib" %> <%@ page extends="org.labkey.api.jsp.JspBase" %> <%! @@ -160,13 +162,15 @@ if (hasParticipantMenu) { Dataset ds = StudyService.get().getDataset(study.getContainer(), participantCommentDatasetId); - hasParticipantMenu = ds != null && ds.canUpdate(user); + TableInfo t = null==ds ? null : ds.getTableInfo(user); + hasParticipantMenu = t != null && t.hasPermission(user, UpdatePermission.class); } if (hasParticipantVisitMenu) { Dataset ds = StudyService.get().getDataset(study.getContainer(), participantVisitCommentDatasetId); - hasParticipantVisitMenu = ds != null && ds.canUpdate(user); + TableInfo t = null==ds ? null : ds.getTableInfo(user); + hasParticipantVisitMenu = t != null && t.hasPermission(user, UpdatePermission.class); } if (hasParticipantMenu || hasParticipantVisitMenu) From f1eb00f716e246428b391f93e9e608f0e7e64b4c Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Mon, 29 Nov 2021 08:02:46 -0800 Subject: [PATCH 350/494] Import javax.mail.* exceptions Original PR: https://github.com/LabKey/platform/pull/2814 --- .../src/org/labkey/specimen/actions/SpecimenController.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/specimen/src/org/labkey/specimen/actions/SpecimenController.java b/specimen/src/org/labkey/specimen/actions/SpecimenController.java index a0d641de..aac93bd2 100644 --- a/specimen/src/org/labkey/specimen/actions/SpecimenController.java +++ b/specimen/src/org/labkey/specimen/actions/SpecimenController.java @@ -157,6 +157,7 @@ import javax.mail.Address; import javax.mail.Message; +import javax.mail.internet.AddressException; import javax.mail.internet.InternetAddress; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; @@ -3746,7 +3747,7 @@ private void sendNotification(DefaultRequestNotification notification, boolean i message.setRecipient(Message.RecipientType.TO, new InternetAddress(email)); MailHelper.send(message, getUser(), getContainer()); } - catch (javax.mail.internet.AddressException | NullPointerException e) + catch (AddressException | NullPointerException e) { errors.reject(SpringActionController.ERROR_MSG, e.getMessage() == null ? e.toString() : e.getMessage()); // Bad address; also InternetAddress constructor can throw null } From 2ac423e1ad4efe994ef098d72115d8c944a73552 Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Wed, 8 Dec 2021 12:12:03 -0800 Subject: [PATCH 351/494] Compile with Java 16 source & target Original PR: https://github.com/LabKey/platform/pull/2813 --- .../src/org/labkey/specimen/view/specimenVisitReport.jsp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/specimen/src/org/labkey/specimen/view/specimenVisitReport.jsp b/specimen/src/org/labkey/specimen/view/specimenVisitReport.jsp index d233858e..0a9b571f 100644 --- a/specimen/src/org/labkey/specimen/view/specimenVisitReport.jsp +++ b/specimen/src/org/labkey/specimen/view/specimenVisitReport.jsp @@ -16,17 +16,16 @@ */ %> <%@ page import="org.apache.commons.lang3.StringUtils"%> -<%@ page import="org.labkey.specimen.report.SpecimenReportTitle" %> -<%@ page import="org.labkey.specimen.report.SpecimenVisitReport" %> -<%@ page import="org.labkey.specimen.report.SpecimenVisitReportParameters" %> <%@ page import="org.labkey.api.study.SpecimenService" %> <%@ 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.util.Pair" %> -<%@ page import="org.labkey.api.util.UniqueID" %> <%@ page import="org.labkey.api.view.HttpView" %> <%@ page import="org.labkey.api.view.JspView" %> +<%@ page import="org.labkey.specimen.report.SpecimenReportTitle" %> +<%@ page import="org.labkey.specimen.report.SpecimenVisitReport" %> +<%@ page import="org.labkey.specimen.report.SpecimenVisitReportParameters" %> <%@ page import="java.util.Collection" %> <%@ page import="java.util.List" %> <%@ page extends="org.labkey.api.jsp.JspBase" %> @@ -34,7 +33,7 @@ JspView me = (JspView) HttpView.currentView(); SpecimenVisitReportParameters bean = me.getModelBean(); Study study = StudyService.get().getStudy(getContainer()); - int tableContainerId = UniqueID.getRequestScopedUID(HttpView.currentRequest()); + int tableContainerId = getRequestScopedUID(); if (study == null) { %> From 20262bf569c01c02ec1f9a6ee216fa9c7148128b Mon Sep 17 00:00:00 2001 From: Josh Eckels Date: Thu, 9 Dec 2021 12:47:47 -0800 Subject: [PATCH 352/494] Issue 44437: Permissions panel is taking minutes to render due to getPolicy.api slowness with thousands of workbooks Original PR: https://github.com/LabKey/platform/pull/2844 --- .../specimen/security/roles/AbstractSpecimenRole.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/specimen/src/org/labkey/specimen/security/roles/AbstractSpecimenRole.java b/specimen/src/org/labkey/specimen/security/roles/AbstractSpecimenRole.java index 7fd976f0..7a7efd70 100644 --- a/specimen/src/org/labkey/specimen/security/roles/AbstractSpecimenRole.java +++ b/specimen/src/org/labkey/specimen/security/roles/AbstractSpecimenRole.java @@ -44,12 +44,15 @@ public boolean isApplicable(SecurityPolicy policy, SecurableResource resource) private boolean branchContainsStudy(Container container) { - if (null != StudyService.get().getStudy(container)) + StudyService ss = StudyService.get(); + if (null != ss && null != ss.getStudy(container)) return true; for (Container child : container.getChildren()) { - if (branchContainsStudy(child)) + // Issue 44437 - don't bother recursing into workbook children for perf reasons since they're not used + // for studies + if (!child.isWorkbook() && branchContainsStudy(child)) return true; } From 7d8f3592c6bf6ba37fcd7d4b0ec68b0f5c573c3c Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Tue, 28 Dec 2021 19:20:05 -0800 Subject: [PATCH 353/494] Issue 44015: Migrate remaining log4j 1.2 usages to 2.x Original PR: https://github.com/LabKey/platform/pull/2899 --- .../src/org/labkey/specimen/actions/SpecimenController.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/specimen/src/org/labkey/specimen/actions/SpecimenController.java b/specimen/src/org/labkey/specimen/actions/SpecimenController.java index aac93bd2..83c09240 100644 --- a/specimen/src/org/labkey/specimen/actions/SpecimenController.java +++ b/specimen/src/org/labkey/specimen/actions/SpecimenController.java @@ -1,8 +1,8 @@ package org.labkey.specimen.actions; import org.apache.commons.lang3.StringUtils; -import org.apache.log4j.LogManager; -import org.apache.log4j.Logger; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.LogManager; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.json.JSONObject; From fc2bc63744fc215bcfd82f5eba13353c3727523d Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Sat, 8 Jan 2022 18:16:41 -0800 Subject: [PATCH 354/494] Issue 44564: Clean up folder import Original PR: https://github.com/LabKey/platform/pull/2925 --- .../src/org/labkey/specimen/pipeline/SpecimenBatch.java | 6 ------ 1 file changed, 6 deletions(-) diff --git a/specimen/src/org/labkey/specimen/pipeline/SpecimenBatch.java b/specimen/src/org/labkey/specimen/pipeline/SpecimenBatch.java index 131b932c..acf9b102 100644 --- a/specimen/src/org/labkey/specimen/pipeline/SpecimenBatch.java +++ b/specimen/src/org/labkey/specimen/pipeline/SpecimenBatch.java @@ -76,12 +76,6 @@ public ActionURL getStatusHref() return PageFlowUtil.urlProvider(ProjectUrls.class).getBeginURL(getInfo().getContainer(), SpecimensPage.PAGE_ID); } - @Override - public File getSpecimenArchive() - { - return _definitionFile; - } - @Override public Path getSpecimenArchivePath() { From f230b3b4430ce75ef1bed350d0bb8312b4b10307 Mon Sep 17 00:00:00 2001 From: labkey-matthewb Date: Mon, 24 Jan 2022 12:31:23 -0800 Subject: [PATCH 355/494] ExcelLoader perf Original PR: https://github.com/LabKey/platform/pull/2942 * ExcelLoader perf * InvalidFormatException * ExcelLoader(InputStream) * remove FileInputStream assert * call DataLoader.close() --- .../labkey/specimen/pipeline/SampleMindedTransformTask.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/specimen/src/org/labkey/specimen/pipeline/SampleMindedTransformTask.java b/specimen/src/org/labkey/specimen/pipeline/SampleMindedTransformTask.java index a15bf4dd..5782cb46 100644 --- a/specimen/src/org/labkey/specimen/pipeline/SampleMindedTransformTask.java +++ b/specimen/src/org/labkey/specimen/pipeline/SampleMindedTransformTask.java @@ -218,9 +218,8 @@ public void transform(Path input, Path output) throws PipelineJobException debug("No such file " + labsFile + " so not parsing supplemental lab information"); } - try (InputStream is = Files.newInputStream(input)) + try (ExcelLoader loader = ExcelLoader.create(input, true, null)) { - ExcelLoader loader = new ExcelLoader(is, true, null); loader.setInferTypes(false); // Create a ZIP archive with the appropriate TSVs From 8942c381664b6db48196994cf9107059f25452f8 Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Wed, 26 Jan 2022 21:45:23 -0800 Subject: [PATCH 356/494] Stop respecting the "Create Specimen Study" experimental feature flag Original PR: https://github.com/LabKey/platform/pull/2982 --- specimen/src/org/labkey/specimen/SpecimenModule.java | 7 +++++++ .../src/org/labkey/specimen/view/manageRequest.jsp | 12 +++++++----- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/specimen/src/org/labkey/specimen/SpecimenModule.java b/specimen/src/org/labkey/specimen/SpecimenModule.java index 191fc7a0..3d6d3b44 100644 --- a/specimen/src/org/labkey/specimen/SpecimenModule.java +++ b/specimen/src/org/labkey/specimen/SpecimenModule.java @@ -29,6 +29,7 @@ import org.labkey.api.query.QueryParam; import org.labkey.api.query.QueryView; import org.labkey.api.security.roles.RoleManager; +import org.labkey.api.settings.AdminConsole; import org.labkey.api.specimen.SpecimenMigrationService; import org.labkey.api.specimen.SpecimenRequestManager; import org.labkey.api.specimen.SpecimensPage; @@ -72,6 +73,8 @@ public class SpecimenModule extends SpringModule { public static final String NAME = "Specimen"; + // TODO: Delete this and all associated code + public static final String CREATE_SPECIMEN_STUDY = "CreateSpecimenStudy"; @Override public String getName() @@ -195,6 +198,10 @@ protected void startupAfterSpringConfig(ModuleContext moduleContext) new SpecimenSchemaImporter(), new SpecimenSettingsImporter() )); + + // TODO: Remove this... we're no longer respecting this flag + AdminConsole.addExperimentalFeatureFlag(CREATE_SPECIMEN_STUDY, "Create Specimen Study", + "Adds a button to the specimen request details page that creates a new child study containing the selected specimens, associated participants, and selected datasets.", false); } @Override diff --git a/specimen/src/org/labkey/specimen/view/manageRequest.jsp b/specimen/src/org/labkey/specimen/view/manageRequest.jsp index c074863d..57ef1cc7 100644 --- a/specimen/src/org/labkey/specimen/view/manageRequest.jsp +++ b/specimen/src/org/labkey/specimen/view/manageRequest.jsp @@ -20,10 +20,9 @@ <%@ page import="org.labkey.api.data.ContainerManager"%> <%@ page import="org.labkey.api.security.User"%> <%@ page import="org.labkey.api.security.UserManager"%> -<%@ page import="org.labkey.api.settings.AppProps"%> -<%@ page import="org.labkey.api.specimen.SpecimenRequestManager" %> -<%@ page import="org.labkey.api.specimen.SpecimenRequestStatus"%> -<%@ page import="org.labkey.api.specimen.Vial" %> +<%@ page import="org.labkey.api.specimen.SpecimenRequestManager"%> +<%@ page import="org.labkey.api.specimen.SpecimenRequestStatus" %> +<%@ page import="org.labkey.api.specimen.Vial"%> <%@ page import="org.labkey.api.specimen.location.LocationImpl" %> <%@ page import="org.labkey.api.specimen.location.LocationManager" %> <%@ page import="org.labkey.api.specimen.model.SpecimenRequestActor" %> @@ -40,6 +39,7 @@ <%@ page import="org.labkey.api.view.JspView" %> <%@ page import="org.labkey.api.view.template.ClientDependencies" %> <%@ page import="org.labkey.specimen.SpecimenManager" %> +<%@ page import="org.labkey.specimen.SpecimenModule" %> <%@ page import="org.labkey.specimen.actions.ManageRequestBean" %> <%@ page import="org.labkey.specimen.actions.ShowSearchAction" %> <%@ page import="org.labkey.specimen.actions.SpecimenController" %> @@ -345,8 +345,10 @@ <% } - if (AppProps.getInstance().isExperimentalFeatureEnabled(SpecimenService.CREATE_SPECIMEN_STUDY)) + // TODO: Remove this and all associated code + if (false) { + String ignored = SpecimenModule.CREATE_SPECIMEN_STUDY; %> <%= button("Create Study").href("javascript:void(0)").onClick("showNewStudyWizard();") %> <% } From 0506e9e4bd0f3de54ba0c83f6c2e5ff1b946a696 Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Mon, 31 Jan 2022 14:51:36 -0800 Subject: [PATCH 357/494] Convert /testAutomation/data/study into a folder archive Original PR: https://github.com/LabKey/testAutomation/pull/990 --- src/org/labkey/test/tests/AliquotTest.java | 5 ++--- .../test/tests/CustomizeEmailTemplateTest.java | 2 +- .../labkey/test/tests/SpecimenCustomizeTest.java | 4 ++-- src/org/labkey/test/tests/SpecimenExportTest.java | 2 +- .../test/tests/SpecimenMultipleImportTest.java | 13 ++++++------- src/org/labkey/test/tests/SpecimenTest.java | 7 +++---- 6 files changed, 15 insertions(+), 18 deletions(-) diff --git a/src/org/labkey/test/tests/AliquotTest.java b/src/org/labkey/test/tests/AliquotTest.java index a601f8df..2a0576e5 100644 --- a/src/org/labkey/test/tests/AliquotTest.java +++ b/src/org/labkey/test/tests/AliquotTest.java @@ -20,7 +20,6 @@ import org.labkey.test.BaseWebDriverTest; import org.labkey.test.Locator; import org.labkey.test.Locators; -import org.labkey.test.TestFileUtils; import org.labkey.test.TestTimeoutException; import org.labkey.test.categories.Daily; import org.labkey.test.categories.Specimen; @@ -36,7 +35,7 @@ public class AliquotTest extends SpecimenBaseTest { protected static final String PROJECT_NAME = "AliquotVerifyProject"; - protected static final File SPECIMEN_ARCHIVE_148 = TestFileUtils.getSampleData("study/specimens/lab148.specimens"); + protected static final File SPECIMEN_ARCHIVE_148 = StudyHelper.getSpecimenArchiveFile("lab148.specimens"); @Override protected String getProjectName() @@ -70,7 +69,7 @@ protected void doCreateSteps() _studyHelper.setupAdvancedRepositoryType(); setupRequestabilityRules(); - setPipelineRoot(StudyHelper.getPipelinePath()); + setPipelineRoot(StudyHelper.getStudySubfolderPath()); startSpecimenImport(1); waitForSpecimenImport(); _studyHelper.createCustomParticipantGroup(getProjectName(), getFolderName(), "Category1", "Participant", null, false, PTIDS[0], PTIDS[1]); diff --git a/src/org/labkey/test/tests/CustomizeEmailTemplateTest.java b/src/org/labkey/test/tests/CustomizeEmailTemplateTest.java index 2347e168..16dc4116 100644 --- a/src/org/labkey/test/tests/CustomizeEmailTemplateTest.java +++ b/src/org/labkey/test/tests/CustomizeEmailTemplateTest.java @@ -96,7 +96,7 @@ protected void doCreateSteps() clickButton("Create Study"); _studyHelper.setupAdvancedRepositoryType(); - setPipelineRoot(StudyHelper.getPipelinePath()); + setPipelineRoot(StudyHelper.getStudySubfolderPath()); startSpecimenImport(1); waitForSpecimenImport(); setupRequestStatuses(); diff --git a/src/org/labkey/test/tests/SpecimenCustomizeTest.java b/src/org/labkey/test/tests/SpecimenCustomizeTest.java index a2243270..b4819591 100644 --- a/src/org/labkey/test/tests/SpecimenCustomizeTest.java +++ b/src/org/labkey/test/tests/SpecimenCustomizeTest.java @@ -43,7 +43,7 @@ public class SpecimenCustomizeTest extends SpecimenBaseTest { protected static final String PROJECT_NAME = "SpecimenCustomizeProject"; - protected static final File SPECIMEN_ARCHIVE = TestFileUtils.getSampleData("study/specimens/Rollup.specimens"); + protected static final File SPECIMEN_ARCHIVE = StudyHelper.getSpecimenArchiveFile("Rollup.specimens"); protected static final String SPECIMEN_AVAILABLE_REASON = "This vial's availability status was set by an administrator. Please contact an administrator for more information."; protected static final String SPECIMEN_UNAVAILABLE_REASON = "This vial is unavailable because it is not currently held by a repository."; @@ -72,7 +72,7 @@ protected void doVerifySteps() { configureSpecimenProperties(); - setPipelineRoot(StudyHelper.getPipelinePath()); + setPipelineRoot(StudyHelper.getStudySubfolderPath()); startSpecimenImport(2, SPECIMEN_ARCHIVE); waitForSpecimenImport(); diff --git a/src/org/labkey/test/tests/SpecimenExportTest.java b/src/org/labkey/test/tests/SpecimenExportTest.java index f2aaac38..6ca36a2b 100644 --- a/src/org/labkey/test/tests/SpecimenExportTest.java +++ b/src/org/labkey/test/tests/SpecimenExportTest.java @@ -66,7 +66,7 @@ protected void doCreateSteps() clickButton("Create Study"); _studyHelper.setupRepositoryType(true, true, true); - setPipelineRoot(StudyHelper.getPipelinePath()); + setPipelineRoot(StudyHelper.getStudySubfolderPath()); startSpecimenImport(1); waitForSpecimenImport(); setPipelineRootToDefault(); diff --git a/src/org/labkey/test/tests/SpecimenMultipleImportTest.java b/src/org/labkey/test/tests/SpecimenMultipleImportTest.java index ab4a3c0d..1fc06941 100644 --- a/src/org/labkey/test/tests/SpecimenMultipleImportTest.java +++ b/src/org/labkey/test/tests/SpecimenMultipleImportTest.java @@ -19,7 +19,6 @@ import org.junit.experimental.categories.Category; import org.labkey.test.BaseWebDriverTest; import org.labkey.test.Locator; -import org.labkey.test.TestFileUtils; import org.labkey.test.categories.Daily; import org.labkey.test.categories.Specimen; import org.labkey.test.util.LogMethod; @@ -33,11 +32,11 @@ public class SpecimenMultipleImportTest extends StudyBaseTest { protected static final String PROJECT_NAME = "AliquotVerifyProject"; - protected static final File SPECIMEN_ARCHIVE_15 = TestFileUtils.getSampleData("study/specimens/lab15.specimens"); - protected static final File SPECIMEN_ARCHIVE_19 = TestFileUtils.getSampleData("study/specimens/lab19.specimens"); - protected static final File SPECIMEN_ARCHIVE_20 = TestFileUtils.getSampleData("study/specimens/lab20.specimens"); - protected static final File SPECIMEN_ARCHIVE_21 = TestFileUtils.getSampleData("study/specimens/lab21.specimens"); - protected static final File SPECIMEN_ARCHIVE_22 = TestFileUtils.getSampleData("study/specimens/lab22.specimens"); + protected static final File SPECIMEN_ARCHIVE_15 = StudyHelper.getSpecimenArchiveFile("lab15.specimens"); + protected static final File SPECIMEN_ARCHIVE_19 = StudyHelper.getSpecimenArchiveFile("lab19.specimens"); + protected static final File SPECIMEN_ARCHIVE_20 = StudyHelper.getSpecimenArchiveFile("lab20.specimens"); + protected static final File SPECIMEN_ARCHIVE_21 = StudyHelper.getSpecimenArchiveFile("lab21.specimens"); + protected static final File SPECIMEN_ARCHIVE_22 = StudyHelper.getSpecimenArchiveFile("lab22.specimens"); @Override protected String getProjectName() @@ -63,7 +62,7 @@ protected void doCreateSteps() clickButton("Create Study"); _studyHelper.setupAdvancedRepositoryType(); - setPipelineRoot(StudyHelper.getPipelinePath()); + setPipelineRoot(StudyHelper.getStudySubfolderPath()); startSpecimenImport(1, SPECIMEN_ARCHIVE_15); startSpecimenImport(2, SPECIMEN_ARCHIVE_19); diff --git a/src/org/labkey/test/tests/SpecimenTest.java b/src/org/labkey/test/tests/SpecimenTest.java index 45e6bdd7..8b05c996 100644 --- a/src/org/labkey/test/tests/SpecimenTest.java +++ b/src/org/labkey/test/tests/SpecimenTest.java @@ -23,7 +23,6 @@ import org.labkey.test.BaseWebDriverTest; import org.labkey.test.Locator; import org.labkey.test.Locators; -import org.labkey.test.TestFileUtils; import org.labkey.test.TestTimeoutException; import org.labkey.test.WebTestHelper; import org.labkey.test.categories.Daily; @@ -65,7 +64,7 @@ public class SpecimenTest extends SpecimenBaseTest { protected static final String PROJECT_NAME = "SpecimenVerifyProject"; - private final File REQUEST_ATTACHMENT = StudyHelper.getStudySampleData("specimens/labs.txt"); + private final File REQUEST_ATTACHMENT = StudyHelper.getSpecimenArchiveFile("labs.txt"); private final PortalHelper _portalHelper = new PortalHelper(this); private final String[] SPECIMEN_IDS = {"AAA07XK5-01", "AAA07XK5-02"}; @@ -107,7 +106,7 @@ protected void doCreateSteps() _studyHelper.setupAdvancedRepositoryType(); setupRequestabilityRules(); - setPipelineRoot(StudyHelper.getPipelinePath()); + setPipelineRoot(StudyHelper.getStudySubfolderPath()); startSpecimenImport(1); waitForSpecimenImport(); checkSpecimenReport(); @@ -965,7 +964,7 @@ private void exportSpecimenTest() @LogMethod private void verifyDrawTimestamp() { - File SPECIMEN_ARCHIVE_DTS = TestFileUtils.getSampleData("study/specimens/dts.specimens"); + File SPECIMEN_ARCHIVE_DTS = StudyHelper.getSpecimenArchiveFile("dts.specimens"); startSpecimenImport(2, SPECIMEN_ARCHIVE_DTS); waitForSpecimenImport(); From d58eed2c75524d80d2ebcd6c91da397763a79dcb Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Mon, 31 Jan 2022 14:51:38 -0800 Subject: [PATCH 358/494] Convert /testAutomation/data/study into a folder archive Original PR: https://github.com/LabKey/platform/pull/2995 --- .../org/labkey/test/tests/study/SpecimenMergeTest.java | 9 ++++----- .../labkey/test/tests/study/SpecimenReplaceTest.java | 10 +++++----- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/study/test/src/org/labkey/test/tests/study/SpecimenMergeTest.java b/study/test/src/org/labkey/test/tests/study/SpecimenMergeTest.java index a1373f23..d77db130 100644 --- a/study/test/src/org/labkey/test/tests/study/SpecimenMergeTest.java +++ b/study/test/src/org/labkey/test/tests/study/SpecimenMergeTest.java @@ -19,7 +19,6 @@ import org.junit.experimental.categories.Category; 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.categories.Specimen; @@ -39,14 +38,14 @@ public class SpecimenMergeTest extends BaseWebDriverTest protected static final String PROJECT_NAME = "SpecimenMergeTest"; protected static final String FOLDER_NAME = "My Study"; - protected static final File LAB19_SPECIMENS = TestFileUtils.getSampleData("study/specimens/lab19.specimens"); - protected static final File LAB20_SPECIMENS = TestFileUtils.getSampleData("study/specimens/lab20.specimens"); - protected static final File LAB21_SPECIMENS = TestFileUtils.getSampleData("study/specimens/lab21.specimens"); + protected static final File LAB19_SPECIMENS = StudyHelper.getSpecimenArchiveFile("lab19.specimens"); + protected static final File LAB20_SPECIMENS = StudyHelper.getSpecimenArchiveFile("lab20.specimens"); + protected static final File LAB21_SPECIMENS = StudyHelper.getSpecimenArchiveFile("lab21.specimens"); protected static final File SPECIMEN_TEMP_DIR = StudyHelper.getStudyTempDir(); protected int pipelineJobCount = 3; - protected final String _studyDataRoot = StudyHelper.getPipelinePath(); + protected final String _studyDataRoot = StudyHelper.getStudySubfolderPath(); @Override public List getAssociatedModules() diff --git a/study/test/src/org/labkey/test/tests/study/SpecimenReplaceTest.java b/study/test/src/org/labkey/test/tests/study/SpecimenReplaceTest.java index da0441a0..a8f9d19c 100644 --- a/study/test/src/org/labkey/test/tests/study/SpecimenReplaceTest.java +++ b/study/test/src/org/labkey/test/tests/study/SpecimenReplaceTest.java @@ -19,9 +19,9 @@ import org.junit.experimental.categories.Category; import org.labkey.test.BaseWebDriverTest; import org.labkey.test.Locator; -import org.labkey.test.TestFileUtils; import org.labkey.test.categories.Daily; import org.labkey.test.categories.Specimen; +import org.labkey.test.util.StudyHelper; import java.io.File; @@ -29,10 +29,10 @@ @BaseWebDriverTest.ClassTimeout(minutes = 6) public class SpecimenReplaceTest extends SpecimenMergeTest { - protected static final File LAB_EDITED_SPECIMENS = TestFileUtils.getSampleData("study/specimens/lab19edit.specimens"); - protected static final File LAB15_SPECIMENS = TestFileUtils.getSampleData("study/specimens/lab15.specimens"); - protected static final File LAB20_SPECIMENS = TestFileUtils.getSampleData("study/specimens/lab20.specimens"); - protected static final File LAB21_SPECIMENS = TestFileUtils.getSampleData("study/specimens/lab21.specimens"); + protected static final File LAB_EDITED_SPECIMENS = StudyHelper.getSpecimenArchiveFile("lab19edit.specimens"); + protected static final File LAB15_SPECIMENS = StudyHelper.getSpecimenArchiveFile("lab15.specimens"); + protected static final File LAB20_SPECIMENS = StudyHelper.getSpecimenArchiveFile("lab20.specimens"); + protected static final File LAB21_SPECIMENS = StudyHelper.getSpecimenArchiveFile("lab21.specimens"); @Override @Test From e285de5770c1e3e4ab45407e579a1cda0cc6d765 Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Sun, 20 Feb 2022 08:01:16 -0800 Subject: [PATCH 359/494] A few cleanups and fixes in study & specimen Original PR: https://github.com/LabKey/platform/pull/3070 --- .../org/labkey/specimen/SpecimenModule.java | 11 + .../labkey/specimen/SpecimenServiceImpl.java | 8 - .../actions/UpdateSpecimenCommentsBean.java | 16 +- .../importer/AbstractSpecimenTask.java | 269 ++++++++++++++++++ .../importer/AbstractSpecimenTaskFactory.java | 57 ++++ .../pipeline/FileAnalysisSpecimenTask.java | 7 +- .../specimen/pipeline/SpecimenPipeline.java | 2 +- .../pipeline/StandaloneSpecimenTask.java | 4 +- .../labkey/specimen/view/manageSpecimens.jsp | 3 +- 9 files changed, 345 insertions(+), 32 deletions(-) create mode 100644 specimen/src/org/labkey/specimen/importer/AbstractSpecimenTask.java create mode 100644 specimen/src/org/labkey/specimen/importer/AbstractSpecimenTaskFactory.java diff --git a/specimen/src/org/labkey/specimen/SpecimenModule.java b/specimen/src/org/labkey/specimen/SpecimenModule.java index 3d6d3b44..cf9062da 100644 --- a/specimen/src/org/labkey/specimen/SpecimenModule.java +++ b/specimen/src/org/labkey/specimen/SpecimenModule.java @@ -25,6 +25,8 @@ import org.labkey.api.data.TableInfo; import org.labkey.api.module.ModuleContext; import org.labkey.api.module.SpringModule; +import org.labkey.api.pipeline.PipelineJob; +import org.labkey.api.pipeline.PipelineJobException; import org.labkey.api.pipeline.PipelineService; import org.labkey.api.query.QueryParam; import org.labkey.api.query.QueryView; @@ -36,10 +38,12 @@ import org.labkey.api.specimen.importer.SpecimenImporter; import org.labkey.api.specimen.model.SpecimenRequestEvent; import org.labkey.api.specimen.model.SpecimenRequestEventType; +import org.labkey.specimen.importer.AbstractSpecimenTask; import org.labkey.api.specimen.view.SpecimenRequestNotificationEmailTemplate; import org.labkey.api.study.SpecimenService; import org.labkey.api.study.StudyInternalService; import org.labkey.api.study.StudyService; +import org.labkey.api.study.importer.SimpleStudyImportContext; import org.labkey.api.study.importer.SimpleStudyImporterRegistry; import org.labkey.api.study.writer.SimpleStudyWriterRegistry; import org.labkey.api.util.emailTemplate.EmailTemplateService; @@ -65,6 +69,7 @@ import org.labkey.specimen.writer.SpecimenSettingsWriter; import org.labkey.specimen.writer.SpecimenWriter; +import java.nio.file.Path; import java.util.Collection; import java.util.Collections; import java.util.List; @@ -174,6 +179,12 @@ public ActionURL getUpdateSpecimenQueryRowURL(Container c, String schemaName, Ta return url; } + + @Override + public void importSpecimenArchive(@Nullable Path inputFile, PipelineJob job, SimpleStudyImportContext ctx, boolean merge, boolean syncParticipantVisit) throws PipelineJobException + { + AbstractSpecimenTask.doImport(inputFile, job, ctx, merge, syncParticipantVisit); + } }); } diff --git a/specimen/src/org/labkey/specimen/SpecimenServiceImpl.java b/specimen/src/org/labkey/specimen/SpecimenServiceImpl.java index e190e53d..88f0d6bc 100644 --- a/specimen/src/org/labkey/specimen/SpecimenServiceImpl.java +++ b/specimen/src/org/labkey/specimen/SpecimenServiceImpl.java @@ -53,9 +53,7 @@ import org.labkey.api.util.Pair; import org.labkey.api.view.ActionURL; import org.labkey.api.view.ViewBackgroundInfo; -import org.labkey.specimen.actions.SpecimenController; import org.labkey.specimen.pipeline.SpecimenReloadJob; -import org.springframework.web.servlet.mvc.Controller; import java.io.IOException; import java.util.ArrayList; @@ -463,10 +461,4 @@ public void fireSpecimensChanged(Container c, User user, Logger logger) for (SpecimenChangeListener l : _changeListeners) l.specimensChanged(c, user, logger); } - - @Override - public Class getManageSpecimenWebPartActionClass() - { - return SpecimenController.ManageSpecimenWebPartAction.class; - } } diff --git a/specimen/src/org/labkey/specimen/actions/UpdateSpecimenCommentsBean.java b/specimen/src/org/labkey/specimen/actions/UpdateSpecimenCommentsBean.java index fe432365..b01e9fcf 100644 --- a/specimen/src/org/labkey/specimen/actions/UpdateSpecimenCommentsBean.java +++ b/specimen/src/org/labkey/specimen/actions/UpdateSpecimenCommentsBean.java @@ -62,21 +62,7 @@ public UpdateSpecimenCommentsBean(ViewContext context, List vials, String _currentFlagState = mixedFlagState || (prevComment != null && prevComment.isQualityControlFlag()); _mixedComments = mixedComments; _mixedFlagState = mixedFlagState; - _participantVisitMap = generateParticipantVisitMap(vials, getStudyRedirectIfNull(context.getContainer())); - } - - @Migrate // Use SpecimenController2.getStudyRedirectIfNull() - @NotNull - private static Study getStudyRedirectIfNull(Container c) - { - Study study = StudyService.get().getStudy(c); - if (null == study) - { - // redirect to the study home page, where admins will see a 'create study' button, - // and non-admins will simply see a message that no study exists. - throw new RedirectException(PageFlowUtil.urlProvider(StudyUrls.class).getBeginURL(c)); - } - return study; + _participantVisitMap = generateParticipantVisitMap(vials, SpecimenController.getStudyRedirectIfNull(context.getContainer())); } protected Map> generateParticipantVisitMap(List vials, Study study) diff --git a/specimen/src/org/labkey/specimen/importer/AbstractSpecimenTask.java b/specimen/src/org/labkey/specimen/importer/AbstractSpecimenTask.java new file mode 100644 index 00000000..ea9130d6 --- /dev/null +++ b/specimen/src/org/labkey/specimen/importer/AbstractSpecimenTask.java @@ -0,0 +1,269 @@ +/* + * Copyright (c) 2009-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.specimen.importer; + +import org.apache.logging.log4j.Logger; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.labkey.api.admin.ImportException; +import org.labkey.api.pipeline.CancelledException; +import org.labkey.api.pipeline.PipelineJob; +import org.labkey.api.pipeline.PipelineJobException; +import org.labkey.api.pipeline.RecordedActionSet; +import org.labkey.api.pipeline.TaskFactory; +import org.labkey.api.specimen.importer.SpecimenImporter; +import org.labkey.api.specimen.pipeline.SpecimenJobSupport; +import org.labkey.api.specimen.writer.SpecimenArchiveDataTypes; +import org.labkey.api.study.SpecimenService; +import org.labkey.api.study.SpecimenTransform; +import org.labkey.api.study.Study; +import org.labkey.api.study.StudyService; +import org.labkey.api.study.importer.SimpleStudyImportContext; +import org.labkey.api.study.model.VisitService; +import org.labkey.api.util.DateUtil; +import org.labkey.api.util.FileType; +import org.labkey.api.util.FileUtil; +import org.labkey.api.writer.FileSystemFile; +import org.labkey.api.writer.VirtualFile; +import org.labkey.api.writer.ZipUtil; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.sql.BatchUpdateException; +import java.util.Date; + +/* +* User: adam +* Date: Aug 31, 2009 +* Time: 9:12:22 AM +*/ +public abstract class AbstractSpecimenTask> extends PipelineJob.Task +{ + public static final FileType ARCHIVE_FILE_TYPE = new FileType(".specimens"); + + protected AbstractSpecimenTask(FactoryType factory, PipelineJob job) + { + super(factory, job); + } + + @Override + @NotNull + public RecordedActionSet run() throws PipelineJobException + { + try + { + PipelineJob job = getJob(); + Path specimenArchive = getSpecimenFile(job); + SimpleStudyImportContext ctx = getImportContext(job); + + doImport(specimenArchive, job, ctx, isMerge(), true, getImportHelper()); + } + catch (CancelledException | PipelineJobException e) + { + throw e; + } + catch (Exception e) + { + throw new PipelineJobException("Error attempting to load specimen archive", e); + } + + return new RecordedActionSet(); + } + + protected Path getSpecimenFile(PipelineJob job) throws Exception + { + SpecimenJobSupport support = job.getJobSupport(SpecimenJobSupport.class); + return support.getSpecimenArchivePath(); + } + + protected SimpleStudyImportContext getImportContext(PipelineJob job) + { + return job.getJobSupport(SpecimenJobSupport.class).getImportContext(); + } + + public static void doImport(@Nullable Path inputFile, PipelineJob job, SimpleStudyImportContext ctx, boolean merge, + boolean syncParticipantVisit) throws PipelineJobException + { + doImport(inputFile, job, ctx, merge, syncParticipantVisit, new DefaultImportHelper()); + } + + private static void doPostTransform(SpecimenTransform transformer, Path inputFile, PipelineJob job) throws PipelineJobException + { + if (transformer.getFileType().isType(inputFile)) + { + if (job != null) + job.setStatus("OPTIONAL POST TRANSFORMING STEP " + transformer.getName() + " DATA"); + File specimenArchive = ARCHIVE_FILE_TYPE.getFile(inputFile.getParent().toFile(), transformer.getFileType().getBaseName(inputFile)); + transformer.postTransform(job, inputFile.toFile(), specimenArchive); + } + } + + public static void doImport(@Nullable Path inputFile, PipelineJob job, SimpleStudyImportContext ctx, boolean merge, + boolean syncParticipantVisit, ImportHelper importHelper) throws PipelineJobException + { + // do nothing if we've specified data types and specimen is not one of them + if (!ctx.isDataTypeSelected(SpecimenArchiveDataTypes.SPECIMENS)) + return; + + try + { + VirtualFile specimenDir; + specimenDir = importHelper.getSpecimenDir(job, ctx, inputFile); + if (specimenDir == null) + return; + + if (job != null) + job.setStatus("PROCESSING SPECIMENS"); + ctx.getLogger().info("Starting specimen import..."); + SpecimenImporter importer = new SpecimenImporter(ctx.getContainer(), ctx.getUser()); + importer.process(specimenDir, merge, ctx, job, syncParticipantVisit); + + // perform any tasks after the transform and import has been completed + String activeImporter = SpecimenService.get().getActiveSpecimenImporter(ctx.getContainer()); + if (null != activeImporter) + { + SpecimenTransform activeTransformer = SpecimenService.get().getSpecimenTransform(activeImporter); + if (activeTransformer != null && activeTransformer.getFileType().isType(inputFile)) + doPostTransform(activeTransformer, inputFile, job); + } + else + { + for (SpecimenTransform transformer : SpecimenService.get().getSpecimenTransforms(ctx.getContainer())) + { + if (transformer.getFileType().isType(inputFile)) + { + doPostTransform(transformer, inputFile, job); + break; + } + } + } + } + catch (CancelledException e) + { + throw e; // rethrow so pipeline marks it canceled + } + catch (Exception e) + { + if (e instanceof BatchUpdateException && null != ((BatchUpdateException)e).getNextException()) + e = ((BatchUpdateException)e).getNextException(); + throw new PipelineJobException(e); + } + finally + { + // do any temp file cleanup + importHelper.afterImport(ctx); + + // Since changing specimens in this study will impact specimens in ancillary studies dependent on this study, + // we need to force a participant/visit refresh in those study containers (if any): + for (Study dependentStudy : StudyService.get().getAncillaryStudies(ctx.getContainer())) + VisitService.get().updateParticipantVisits(dependentStudy, ctx.getUser()); + } + } + + protected boolean isMerge() + { + return getJob().getJobSupport(SpecimenJobSupport.class).isMerge(); + } + + public ImportHelper getImportHelper() + { + return new DefaultImportHelper(); + } + + public interface ImportHelper + { + VirtualFile getSpecimenDir(PipelineJob job, SimpleStudyImportContext ctx, @Nullable Path inputFile) throws IOException, ImportException, PipelineJobException; + void afterImport(SimpleStudyImportContext ctx); + } + + protected static class DefaultImportHelper implements ImportHelper + { + private Path _unzipDir; + + @Override + public VirtualFile getSpecimenDir(PipelineJob job, SimpleStudyImportContext ctx, @Nullable Path inputFile) throws IOException, ImportException, PipelineJobException + { + // backwards compatibility, if we are given a specimen archive as a zip file, we need to extract it + if (inputFile != null) + { + // Might need to transform to a file type that we know how to import + Path specimenArchive = inputFile; + + for (SpecimenTransform transformer : SpecimenService.get().getSpecimenTransforms(ctx.getContainer())) + { + if (transformer.getFileType().isType(inputFile.getFileName().toString())) + { + if (job != null) + job.setStatus("TRANSFORMING " + transformer.getName() + " DATA"); + specimenArchive = ARCHIVE_FILE_TYPE.getPath(inputFile.getParent(), transformer.getFileType().getBaseName(inputFile)); + transformer.transform(job, inputFile, specimenArchive); + break; + } + } + + if (null == specimenArchive) + { + ctx.getLogger().info("No specimen archive"); + return null; + } + else + { + if (job != null) + job.setStatus("UNZIPPING SPECIMEN ARCHIVE"); + + ctx.getLogger().info("Unzipping specimen archive " + specimenArchive); + String tempDirName = DateUtil.formatDateTime(new Date(), "yyMMddHHmmssSSS"); + _unzipDir = specimenArchive.getParent().resolve(tempDirName); + ZipUtil.unzipToDirectory(specimenArchive, _unzipDir, ctx.getLogger()); + + ctx.getLogger().info("Archive unzipped to " + _unzipDir); + return new FileSystemFile(_unzipDir); + } + } + else + { + // the specimen files must already be "extracted" in the specimens directory + return ctx.getDir("specimens"); + } + } + + @Override + public void afterImport(SimpleStudyImportContext ctx) + { + if (_unzipDir != null) + delete(_unzipDir, ctx); + } + + protected void delete(Path path, SimpleStudyImportContext ctx) + { + Logger log = ctx.getLogger(); + if (Files.isDirectory(path)) + { + try + { + FileUtil.deleteDir(path); + } + catch (IOException e) + { + log.error("Error deleting files from " + path, e); + } + } + } + } +} diff --git a/specimen/src/org/labkey/specimen/importer/AbstractSpecimenTaskFactory.java b/specimen/src/org/labkey/specimen/importer/AbstractSpecimenTaskFactory.java new file mode 100644 index 00000000..9084ee69 --- /dev/null +++ b/specimen/src/org/labkey/specimen/importer/AbstractSpecimenTaskFactory.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2009-2010 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.specimen.importer; + +import org.labkey.api.pipeline.AbstractTaskFactory; +import org.labkey.api.pipeline.AbstractTaskFactorySettings; +import org.labkey.api.pipeline.PipelineJob; +import org.labkey.api.util.FileType; + +import java.util.Collections; +import java.util.List; + +public abstract class AbstractSpecimenTaskFactory> extends AbstractTaskFactory +{ + public AbstractSpecimenTaskFactory(Class namespaceClass) + { + super(namespaceClass); + } + + @Override + public List getInputTypes() + { + return Collections.emptyList(); + } + + @Override + public List getProtocolActionNames() + { + return Collections.emptyList(); + } + + @Override + public String getStatusName() + { + return "LOAD SPECIMENS"; + } + + @Override + public boolean isJobComplete(PipelineJob job) + { + return false; + } +} diff --git a/specimen/src/org/labkey/specimen/pipeline/FileAnalysisSpecimenTask.java b/specimen/src/org/labkey/specimen/pipeline/FileAnalysisSpecimenTask.java index cffad094..560dc029 100644 --- a/specimen/src/org/labkey/specimen/pipeline/FileAnalysisSpecimenTask.java +++ b/specimen/src/org/labkey/specimen/pipeline/FileAnalysisSpecimenTask.java @@ -1,6 +1,5 @@ package org.labkey.specimen.pipeline; -import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.BooleanUtils; import org.apache.tika.detect.DefaultDetector; import org.apache.tika.io.TikaInputStream; @@ -12,15 +11,13 @@ import org.labkey.api.pipeline.PipelineJob; import org.labkey.api.pipeline.PipelineJobException; import org.labkey.api.pipeline.file.FileAnalysisJobSupport; -import org.labkey.api.specimen.pipeline.AbstractSpecimenTask; -import org.labkey.api.specimen.pipeline.AbstractSpecimenTaskFactory; +import org.labkey.specimen.importer.AbstractSpecimenTask; +import org.labkey.specimen.importer.AbstractSpecimenTaskFactory; import org.labkey.api.study.importer.SimpleStudyImportContext; import org.labkey.api.util.DateUtil; import org.labkey.api.writer.FileSystemFile; import org.labkey.api.writer.VirtualFile; -import java.io.File; -import java.io.FileInputStream; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; diff --git a/specimen/src/org/labkey/specimen/pipeline/SpecimenPipeline.java b/specimen/src/org/labkey/specimen/pipeline/SpecimenPipeline.java index babbc742..dad9ceec 100644 --- a/specimen/src/org/labkey/specimen/pipeline/SpecimenPipeline.java +++ b/specimen/src/org/labkey/specimen/pipeline/SpecimenPipeline.java @@ -5,7 +5,7 @@ import org.labkey.api.pipeline.PipelineDirectory; import org.labkey.api.pipeline.PipelineProvider; import org.labkey.api.security.permissions.InsertPermission; -import org.labkey.api.specimen.pipeline.AbstractSpecimenTask; +import org.labkey.specimen.importer.AbstractSpecimenTask; import org.labkey.api.study.SpecimenService; import org.labkey.api.study.SpecimenTransform; import org.labkey.api.study.StudyService; diff --git a/specimen/src/org/labkey/specimen/pipeline/StandaloneSpecimenTask.java b/specimen/src/org/labkey/specimen/pipeline/StandaloneSpecimenTask.java index 16895246..381fe077 100644 --- a/specimen/src/org/labkey/specimen/pipeline/StandaloneSpecimenTask.java +++ b/specimen/src/org/labkey/specimen/pipeline/StandaloneSpecimenTask.java @@ -17,8 +17,8 @@ package org.labkey.specimen.pipeline; import org.labkey.api.pipeline.PipelineJob; -import org.labkey.api.specimen.pipeline.AbstractSpecimenTask; -import org.labkey.api.specimen.pipeline.AbstractSpecimenTaskFactory; +import org.labkey.specimen.importer.AbstractSpecimenTask; +import org.labkey.specimen.importer.AbstractSpecimenTaskFactory; /* * User: adam diff --git a/specimen/src/org/labkey/specimen/view/manageSpecimens.jsp b/specimen/src/org/labkey/specimen/view/manageSpecimens.jsp index 9ab3633b..0a11aa7b 100644 --- a/specimen/src/org/labkey/specimen/view/manageSpecimens.jsp +++ b/specimen/src/org/labkey/specimen/view/manageSpecimens.jsp @@ -40,6 +40,7 @@ <%@ page import="org.labkey.specimen.actions.SpecimenController.ManageRepositorySettingsAction" %> <%@ page import="org.labkey.specimen.actions.SpecimenController.ManageRequestInputsAction" %> <%@ page import="org.labkey.specimen.actions.SpecimenController.ManageSpecimenCommentsAction" %> +<%@ page import="org.labkey.specimen.actions.SpecimenController.ManageSpecimenWebPartAction" %> <%@ page import="org.labkey.specimen.actions.SpecimenController.ManageStatusesAction" %> <%@ page import="java.util.Collection" %> <%@ page extends="org.labkey.api.jsp.JspBase" %> @@ -121,7 +122,7 @@ Specimen Web Part Configure the specimen groupings in the specimen web part - <%= link("Configure Specimen Groupings", SpecimenService.get().getManageSpecimenWebPartActionClass()) %> + <%= link("Configure Specimen Groupings", ManageSpecimenWebPartAction.class) %> <% if (numberOfTransforms == 1) { %> From 13b0d5474b06064d8010a2aaf7d5891fd057d1d3 Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Tue, 22 Feb 2022 16:54:15 -0800 Subject: [PATCH 360/494] Move more specimen code into specimen module Original PR: https://github.com/LabKey/platform/pull/3077 --- specimen/resources/notification.txt | 37 - .../specimen/AmbiguousLocationException.java | 39 + .../org/labkey/specimen/RequestEventType.java | 28 + .../labkey/specimen/RequestedSpecimens.java | 60 + .../SpecimenCommentAuditDomainKind.java | 46 + .../specimen/SpecimenCommentAuditEvent.java | 42 + .../SpecimenCommentAuditProvider.java | 6 +- .../org/labkey/specimen/SpecimenManager.java | 7 +- .../org/labkey/specimen/SpecimenModule.java | 42 +- .../SpecimenRequestContainerListener.java | 44 + .../specimen/SpecimenRequestManager.java | 1294 +++++++++++++++++ .../specimen/actions/ManageReqsBean.java | 10 +- .../specimen/actions/ManageRequestBean.java | 14 +- .../actions/ManageRequestInputsBean.java | 2 +- .../actions/ShowGroupMembersAction.java | 6 +- .../actions/SpecimenApiController.java | 12 +- .../specimen/actions/SpecimenController.java | 34 +- .../actions/ViewRequestsHeaderBean.java | 4 +- .../importer/AbstractSpecimenTask.java | 2 +- .../importer/SpecimenSchemaImporter.java | 2 +- .../importer/SpecimenSettingsImporter.java | 18 +- .../labkey/specimen/model/AdditiveType.java | 111 ++ .../labkey/specimen/model/DerivativeType.java | 117 ++ .../model/ExtendedSpecimenRequestView.java | 56 + .../specimen/model/SpecimenRequestActor.java | 156 ++ .../specimen/model/SpecimenRequestEvent.java | 163 +++ .../model/SpecimenRequestEventType.java | 47 + .../ActorNotificationRecipientSet.java | 5 +- .../DefaultRequestNotification.java | 147 ++ .../NotificationRecipientSet.java | 120 ++ .../query/SpecimenRequestQueryView.java | 4 +- .../specimen/requirements/DefaultActor.java | 187 +++ .../requirements/DefaultRequirement.java | 57 + .../DefaultRequirementProvider.java | 247 ++++ .../specimen/requirements/Requirement.java | 48 + .../requirements/RequirementActor.java | 53 + .../requirements/RequirementOwner.java | 31 + .../requirements/RequirementProvider.java | 60 + .../requirements/RequirementType.java | 27 + .../requirements/SpecimenRequest.java | 225 +++ .../SpecimenRequestRequirement.java | 261 ++++ .../SpecimenRequestRequirementProvider.java | 102 ++ .../SpecimenRequestRequirementType.java | 118 ++ .../permissions/ManageRequestsPermission.java | 34 + .../roles/SpecimenCoordinatorRole.java | 4 +- .../specimen/view/NotificationBean.java | 122 ++ ...cimenRequestNotificationEmailTemplate.java | 197 +++ .../labkey/specimen/view/manageActorOrder.jsp | 8 +- .../org/labkey/specimen/view/manageActors.jsp | 8 +- .../specimen/view/manageDefaultReqs.jsp | 12 +- .../specimen/view/manageNotifications.jsp | 2 +- .../labkey/specimen/view/manageRequest.jsp | 41 +- .../specimen/view/manageRequestInputs.jsp | 2 +- .../specimen/view/manageRequestStatus.jsp | 4 +- .../specimen/view/manageRequirement.jsp | 4 +- .../labkey/specimen/view/manageSpecimens.jsp | 4 +- .../specimen/view/manageStatusOrder.jsp | 5 +- .../labkey/specimen/view/manageStatuses.jsp | 11 +- .../labkey/specimen/view/requestSpecimens.jsp | 4 +- .../writer/SpecimenArchiveDataTypes.java | 14 + .../writer/SpecimenArchiveWriter.java | 3 +- .../writer/SpecimenSettingsWriter.java | 13 +- .../specimen/writer/SpecimenWriter.java | 16 - 63 files changed, 4391 insertions(+), 208 deletions(-) delete mode 100644 specimen/resources/notification.txt create mode 100644 specimen/src/org/labkey/specimen/AmbiguousLocationException.java create mode 100644 specimen/src/org/labkey/specimen/RequestEventType.java create mode 100644 specimen/src/org/labkey/specimen/RequestedSpecimens.java create mode 100644 specimen/src/org/labkey/specimen/SpecimenCommentAuditDomainKind.java create mode 100644 specimen/src/org/labkey/specimen/SpecimenCommentAuditEvent.java create mode 100644 specimen/src/org/labkey/specimen/SpecimenRequestContainerListener.java create mode 100644 specimen/src/org/labkey/specimen/SpecimenRequestManager.java create mode 100644 specimen/src/org/labkey/specimen/model/AdditiveType.java create mode 100644 specimen/src/org/labkey/specimen/model/DerivativeType.java create mode 100644 specimen/src/org/labkey/specimen/model/ExtendedSpecimenRequestView.java create mode 100644 specimen/src/org/labkey/specimen/model/SpecimenRequestActor.java create mode 100644 specimen/src/org/labkey/specimen/model/SpecimenRequestEvent.java create mode 100644 specimen/src/org/labkey/specimen/model/SpecimenRequestEventType.java create mode 100644 specimen/src/org/labkey/specimen/notifications/DefaultRequestNotification.java create mode 100644 specimen/src/org/labkey/specimen/notifications/NotificationRecipientSet.java create mode 100644 specimen/src/org/labkey/specimen/requirements/DefaultActor.java create mode 100644 specimen/src/org/labkey/specimen/requirements/DefaultRequirement.java create mode 100644 specimen/src/org/labkey/specimen/requirements/DefaultRequirementProvider.java create mode 100644 specimen/src/org/labkey/specimen/requirements/Requirement.java create mode 100644 specimen/src/org/labkey/specimen/requirements/RequirementActor.java create mode 100644 specimen/src/org/labkey/specimen/requirements/RequirementOwner.java create mode 100644 specimen/src/org/labkey/specimen/requirements/RequirementProvider.java create mode 100644 specimen/src/org/labkey/specimen/requirements/RequirementType.java create mode 100644 specimen/src/org/labkey/specimen/requirements/SpecimenRequest.java create mode 100644 specimen/src/org/labkey/specimen/requirements/SpecimenRequestRequirement.java create mode 100644 specimen/src/org/labkey/specimen/requirements/SpecimenRequestRequirementProvider.java create mode 100644 specimen/src/org/labkey/specimen/requirements/SpecimenRequestRequirementType.java create mode 100644 specimen/src/org/labkey/specimen/security/permissions/ManageRequestsPermission.java create mode 100644 specimen/src/org/labkey/specimen/view/NotificationBean.java create mode 100644 specimen/src/org/labkey/specimen/view/SpecimenRequestNotificationEmailTemplate.java create mode 100644 specimen/src/org/labkey/specimen/writer/SpecimenArchiveDataTypes.java diff --git a/specimen/resources/notification.txt b/specimen/resources/notification.txt deleted file mode 100644 index 96d1f3aa..00000000 --- a/specimen/resources/notification.txt +++ /dev/null @@ -1,37 +0,0 @@ -
-
- Specimen request #^specimenRequestNumber^ was ^simpleStatus^ in ^studyName^. -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - ^attachments|^ -
Request Details
Specimen Request^specimenRequestNumber^
Destination^destinationLocation^
Status^status^
Modified by^modifiedBy^
Action^action^
Attachments%s
-^comments|

Current Comments
%s

^ -

- ^requestDescription^ -

-^specimenList^ diff --git a/specimen/src/org/labkey/specimen/AmbiguousLocationException.java b/specimen/src/org/labkey/specimen/AmbiguousLocationException.java new file mode 100644 index 00000000..756ac71a --- /dev/null +++ b/specimen/src/org/labkey/specimen/AmbiguousLocationException.java @@ -0,0 +1,39 @@ +package org.labkey.specimen; + +import org.labkey.api.data.Container; +import org.labkey.api.specimen.location.LocationImpl; +import org.labkey.api.specimen.location.LocationManager; + +import java.util.Collection; + +public class AmbiguousLocationException extends Exception +{ + private final Container _container; + private final Collection _possibleLocationIds; + + private LocationImpl[] _possibleLocations = null; + + public AmbiguousLocationException(Container container, Collection possibleLocationIds) + { + _container = container; + _possibleLocationIds = possibleLocationIds; + } + + public Collection getPossibleLocationIds() + { + return _possibleLocationIds; + } + + public LocationImpl[] getPossibleLocations() + { + if (_possibleLocations == null) + { + _possibleLocations = new LocationImpl[_possibleLocationIds.size()]; + int idx = 0; + + for (Integer id : _possibleLocationIds) + _possibleLocations[idx++] = LocationManager.get().getLocation(_container, id.intValue()); + } + return _possibleLocations; + } +} diff --git a/specimen/src/org/labkey/specimen/RequestEventType.java b/specimen/src/org/labkey/specimen/RequestEventType.java new file mode 100644 index 00000000..e2c42d38 --- /dev/null +++ b/specimen/src/org/labkey/specimen/RequestEventType.java @@ -0,0 +1,28 @@ +package org.labkey.specimen; + +public enum RequestEventType +{ + REQUEST_CREATED("Request Created"), + REQUEST_STATUS_CHANGED("Request Status Changed"), + REQUIREMENT_ADDED("Requirement Created"), + REQUIREMENT_REMOVED("Requirement Removed"), + REQUIREMENT_UPDATED("Requirement Updated"), + REQUEST_UPDATED("Request Updated"), + SPECIMEN_ADDED("Specimen Added"), + SPECIMEN_REMOVED("Specimen Removed"), + SPECIMEN_LIST_GENERATED("Specimen List Generated"), + COMMENT_ADDED("Comment/Attachment(s) Added"), + NOTIFICATION_SENT("Notification Sent"); + + private final String _displayText; + + RequestEventType(String displayText) + { + _displayText = displayText; + } + + public String getDisplayText() + { + return _displayText; + } +} diff --git a/specimen/src/org/labkey/specimen/RequestedSpecimens.java b/specimen/src/org/labkey/specimen/RequestedSpecimens.java new file mode 100644 index 00000000..d03ad904 --- /dev/null +++ b/specimen/src/org/labkey/specimen/RequestedSpecimens.java @@ -0,0 +1,60 @@ +package org.labkey.specimen; + +import org.labkey.api.data.Container; +import org.labkey.api.specimen.Vial; +import org.labkey.api.specimen.location.LocationManager; +import org.labkey.api.study.Location; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; + +public class RequestedSpecimens +{ + private final Collection _providingLocationIds; + private final List _vials; + + private List _providingLocations; + + public RequestedSpecimens(List vials, Collection providingLocationIds) + { + _vials = vials; + _providingLocationIds = providingLocationIds; + } + + public RequestedSpecimens(List vials) + { + _vials = vials; + _providingLocationIds = new HashSet<>(); + if (vials != null) + { + for (Vial vial : vials) + _providingLocationIds.add(vial.getCurrentLocation()); + } + } + + public List getProvidingLocations() + { + if (_providingLocations == null) + { + if (_vials == null || _vials.size() == 0) + _providingLocations = Collections.emptyList(); + else + { + Container container = _vials.get(0).getContainer(); + _providingLocations = new ArrayList<>(_providingLocationIds.size()); + + for (Integer locationId : _providingLocationIds) + _providingLocations.add(LocationManager.get().getLocation(container, locationId.intValue())); + } + } + return _providingLocations; + } + + public List getVials() + { + return _vials; + } +} diff --git a/specimen/src/org/labkey/specimen/SpecimenCommentAuditDomainKind.java b/specimen/src/org/labkey/specimen/SpecimenCommentAuditDomainKind.java new file mode 100644 index 00000000..c3ec485d --- /dev/null +++ b/specimen/src/org/labkey/specimen/SpecimenCommentAuditDomainKind.java @@ -0,0 +1,46 @@ +package org.labkey.specimen; + +import org.labkey.api.audit.query.AbstractAuditDomainKind; +import org.labkey.api.exp.PropertyDescriptor; +import org.labkey.api.exp.PropertyType; + +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.Set; + +public class SpecimenCommentAuditDomainKind extends AbstractAuditDomainKind +{ + public static final String NAME = "SpecimenCommentAuditDomain"; + public static final String SPECIMEN_COMMENT_EVENT = "SpecimenCommentEvent"; + public static final String NAMESPACE_PREFIX = "Audit-" + NAME; + public static final String COLUMN_NAME_VIAL_ID = "VialId"; + + private final Set _fields; + + public SpecimenCommentAuditDomainKind() + { + super(SPECIMEN_COMMENT_EVENT); + + Set fields = new LinkedHashSet<>(); + fields.add(createPropertyDescriptor(COLUMN_NAME_VIAL_ID, PropertyType.STRING)); + _fields = Collections.unmodifiableSet(fields); + } + + @Override + public Set getProperties() + { + return _fields; + } + + @Override + protected String getNamespacePrefix() + { + return NAMESPACE_PREFIX; + } + + @Override + public String getKindName() + { + return NAME; + } +} diff --git a/specimen/src/org/labkey/specimen/SpecimenCommentAuditEvent.java b/specimen/src/org/labkey/specimen/SpecimenCommentAuditEvent.java new file mode 100644 index 00000000..daaa2966 --- /dev/null +++ b/specimen/src/org/labkey/specimen/SpecimenCommentAuditEvent.java @@ -0,0 +1,42 @@ +package org.labkey.specimen; + +import org.labkey.api.audit.AuditTypeEvent; + +import java.util.LinkedHashMap; +import java.util.Map; + +import static org.labkey.specimen.SpecimenCommentAuditDomainKind.SPECIMEN_COMMENT_EVENT; + +public class SpecimenCommentAuditEvent extends AuditTypeEvent +{ + private String _vialId; + + public SpecimenCommentAuditEvent() + { + super(); + } + + public SpecimenCommentAuditEvent(String container, String comment) + { + super(SPECIMEN_COMMENT_EVENT, container, comment); + } + + public String getVialId() + { + return _vialId; + } + + public void setVialId(String vialId) + { + _vialId = vialId; + } + + @Override + public Map getAuditLogMessageElements() + { + Map elements = new LinkedHashMap<>(); + elements.put("vialId", getVialId()); + elements.putAll(super.getAuditLogMessageElements()); + return elements; + } +} diff --git a/specimen/src/org/labkey/specimen/SpecimenCommentAuditProvider.java b/specimen/src/org/labkey/specimen/SpecimenCommentAuditProvider.java index 52cf835e..d5a56268 100644 --- a/specimen/src/org/labkey/specimen/SpecimenCommentAuditProvider.java +++ b/specimen/src/org/labkey/specimen/SpecimenCommentAuditProvider.java @@ -31,8 +31,6 @@ import org.labkey.api.data.TableInfo; import org.labkey.api.query.FieldKey; import org.labkey.api.query.UserSchema; -import org.labkey.api.specimen.SpecimenCommentAuditDomainKind; -import org.labkey.api.specimen.SpecimenCommentAuditEvent; import org.labkey.api.util.Link; import org.labkey.api.view.ActionURL; import org.labkey.specimen.actions.SpecimenController; @@ -43,8 +41,8 @@ import java.util.Map; import java.util.Set; -import static org.labkey.api.specimen.SpecimenCommentAuditDomainKind.COLUMN_NAME_VIAL_ID; -import static org.labkey.api.specimen.SpecimenCommentAuditDomainKind.SPECIMEN_COMMENT_EVENT; +import static org.labkey.specimen.SpecimenCommentAuditDomainKind.COLUMN_NAME_VIAL_ID; +import static org.labkey.specimen.SpecimenCommentAuditDomainKind.SPECIMEN_COMMENT_EVENT; /** * User: klum diff --git a/specimen/src/org/labkey/specimen/SpecimenManager.java b/specimen/src/org/labkey/specimen/SpecimenManager.java index b7779fad..059fa566 100644 --- a/specimen/src/org/labkey/specimen/SpecimenManager.java +++ b/specimen/src/org/labkey/specimen/SpecimenManager.java @@ -18,7 +18,6 @@ import org.labkey.api.query.QueryService; import org.labkey.api.query.UserSchema; import org.labkey.api.security.User; -import org.labkey.api.specimen.SpecimenCommentAuditEvent; import org.labkey.api.specimen.SpecimenManagerNew; import org.labkey.api.specimen.SpecimenQuerySchema; import org.labkey.api.specimen.SpecimenRequestException; @@ -26,9 +25,6 @@ import org.labkey.api.specimen.Vial; import org.labkey.api.specimen.location.LocationImpl; import org.labkey.api.specimen.location.LocationManager; -import org.labkey.api.specimen.model.AdditiveType; -import org.labkey.api.specimen.model.DerivativeType; -import org.labkey.api.specimen.model.ExtendedSpecimenRequestView; import org.labkey.api.specimen.model.SpecimenComment; import org.labkey.api.study.Cohort; import org.labkey.api.study.Study; @@ -38,6 +34,9 @@ import org.labkey.api.util.HtmlString; import org.labkey.api.util.Path; import org.labkey.api.view.ViewContext; +import org.labkey.specimen.model.AdditiveType; +import org.labkey.specimen.model.DerivativeType; +import org.labkey.specimen.model.ExtendedSpecimenRequestView; import java.util.ArrayList; import java.util.Calendar; diff --git a/specimen/src/org/labkey/specimen/SpecimenModule.java b/specimen/src/org/labkey/specimen/SpecimenModule.java index cf9062da..773eee84 100644 --- a/specimen/src/org/labkey/specimen/SpecimenModule.java +++ b/specimen/src/org/labkey/specimen/SpecimenModule.java @@ -30,16 +30,12 @@ import org.labkey.api.pipeline.PipelineService; import org.labkey.api.query.QueryParam; import org.labkey.api.query.QueryView; +import org.labkey.api.security.User; import org.labkey.api.security.roles.RoleManager; import org.labkey.api.settings.AdminConsole; import org.labkey.api.specimen.SpecimenMigrationService; -import org.labkey.api.specimen.SpecimenRequestManager; import org.labkey.api.specimen.SpecimensPage; import org.labkey.api.specimen.importer.SpecimenImporter; -import org.labkey.api.specimen.model.SpecimenRequestEvent; -import org.labkey.api.specimen.model.SpecimenRequestEventType; -import org.labkey.specimen.importer.AbstractSpecimenTask; -import org.labkey.api.specimen.view.SpecimenRequestNotificationEmailTemplate; import org.labkey.api.study.SpecimenService; import org.labkey.api.study.StudyInternalService; import org.labkey.api.study.StudyService; @@ -52,16 +48,20 @@ import org.labkey.api.view.WebPartFactory; import org.labkey.specimen.actions.SpecimenApiController; import org.labkey.specimen.actions.SpecimenController; +import org.labkey.specimen.importer.AbstractSpecimenTask; import org.labkey.specimen.importer.DefaultSpecimenImportStrategyFactory; import org.labkey.specimen.importer.SpecimenSchemaImporter; import org.labkey.specimen.importer.SpecimenSettingsImporter; +import org.labkey.specimen.model.SpecimenRequestEventType; import org.labkey.specimen.pipeline.SampleMindedTransform; import org.labkey.specimen.pipeline.SampleMindedTransformTask; import org.labkey.specimen.pipeline.SpecimenPipeline; +import org.labkey.specimen.requirements.SpecimenRequestRequirementProvider; import org.labkey.specimen.security.roles.SpecimenCoordinatorRole; import org.labkey.specimen.security.roles.SpecimenRequesterRole; import org.labkey.specimen.view.ManageSpecimenView; import org.labkey.specimen.view.SpecimenReportWebPartFactory; +import org.labkey.specimen.view.SpecimenRequestNotificationEmailTemplate; import org.labkey.specimen.view.SpecimenSearchWebPartFactory; import org.labkey.specimen.view.SpecimenToolsWebPartFactory; import org.labkey.specimen.view.SpecimenWebPartFactory; @@ -142,12 +142,6 @@ public ActionURL getSelectedSpecimensURL(Container c) return new ActionURL(SpecimenController.SelectedSpecimensAction.class, c); } - @Override - public ActionURL getSpecimenRequestEventDownloadURL(SpecimenRequestEvent event, String name) - { - return SpecimenController.getDownloadURL(event, name); - } - @Override public ActionURL getSpecimensURL(Container c) { @@ -185,13 +179,37 @@ public void importSpecimenArchive(@Nullable Path inputFile, PipelineJob job, Sim { AbstractSpecimenTask.doImport(inputFile, job, ctx, merge, syncParticipantVisit); } + + @Override + public void clearRequestCaches(Container c) + { + SpecimenRequestManager.get().clearCaches(c); + } + + @Override + public void clearGroupedValuesForColumn(Container container) + { + SpecimenRequestManager.get().clearGroupedValuesForColumn(container); + } + + @Override + public void updateVialCounts(Container container, User user) + { + SpecimenRequestManager.get().updateVialCounts(container, user); + } + + @Override + public void purgeRequestRequirementsAndActors(Container container) + { + SpecimenRequestRequirementProvider.get().purgeContainer(container); + } }); } @Override protected void startupAfterSpringConfig(ModuleContext moduleContext) { - ContainerManager.addContainerListener(SpecimenRequestManager.get()); + ContainerManager.addContainerListener(new SpecimenRequestContainerListener()); StudyService.get().registerStudyTabProvider(tabs->tabs.add(new SpecimensPage("Specimen Data"))); SpecimenService.get().registerSpecimenImportStrategyFactory(new DefaultSpecimenImportStrategyFactory()); diff --git a/specimen/src/org/labkey/specimen/SpecimenRequestContainerListener.java b/specimen/src/org/labkey/specimen/SpecimenRequestContainerListener.java new file mode 100644 index 00000000..a9dfc275 --- /dev/null +++ b/specimen/src/org/labkey/specimen/SpecimenRequestContainerListener.java @@ -0,0 +1,44 @@ +package org.labkey.specimen; + +import org.jetbrains.annotations.NotNull; +import org.labkey.api.data.Container; +import org.labkey.api.data.ContainerManager; +import org.labkey.api.security.User; + +import java.beans.PropertyChangeEvent; +import java.util.Collection; +import java.util.Collections; + +public class SpecimenRequestContainerListener implements ContainerManager.ContainerListener +{ + @Override + public void containerCreated(Container c, User user) + { + SpecimenRequestManager.get().clearCaches(c); + } + + @Override + public void containerDeleted(Container c, User user) + { + SpecimenRequestManager.get().clearCaches(c); + } + + @Override + public void containerMoved(Container c, Container oldParent, User user) + { + SpecimenRequestManager.get().clearCaches(c); + } + + @NotNull + @Override + public Collection canMove(Container c, Container newParent, User user) + { + return Collections.emptyList(); + } + + @Override + public void propertyChange(PropertyChangeEvent evt) + { + SpecimenRequestManager.get().clearCaches((Container)evt.getSource()); + } +} diff --git a/specimen/src/org/labkey/specimen/SpecimenRequestManager.java b/specimen/src/org/labkey/specimen/SpecimenRequestManager.java new file mode 100644 index 00000000..973c8b18 --- /dev/null +++ b/specimen/src/org/labkey/specimen/SpecimenRequestManager.java @@ -0,0 +1,1294 @@ +package org.labkey.specimen; + +import org.apache.commons.collections4.comparators.ComparableComparator; +import org.apache.commons.lang3.StringUtils; +import org.jetbrains.annotations.NotNull; +import org.labkey.api.action.SpringActionController; +import org.labkey.api.attachments.AttachmentFile; +import org.labkey.api.attachments.AttachmentService; +import org.labkey.api.cache.CacheManager; +import org.labkey.api.data.ColumnInfo; +import org.labkey.api.data.Container; +import org.labkey.api.data.DatabaseCache; +import org.labkey.api.data.DbScope; +import org.labkey.api.data.RuntimeSQLException; +import org.labkey.api.data.SQLFragment; +import org.labkey.api.data.SimpleFilter; +import org.labkey.api.data.SqlExecutor; +import org.labkey.api.data.SqlSelector; +import org.labkey.api.data.Table; +import org.labkey.api.data.TableInfo; +import org.labkey.api.data.TableResultSet; +import org.labkey.api.exp.Lsid; +import org.labkey.api.exp.ObjectProperty; +import org.labkey.api.exp.OntologyManager; +import org.labkey.api.exp.PropertyType; +import org.labkey.api.query.FieldKey; +import org.labkey.api.query.QueryService; +import org.labkey.api.query.UserSchema; +import org.labkey.api.query.ValidationException; +import org.labkey.api.security.User; +import org.labkey.api.settings.AppProps; +import org.labkey.api.specimen.SpecimenManagerNew; +import org.labkey.api.specimen.SpecimenQuerySchema; +import org.labkey.api.specimen.SpecimenRequestException; +import org.labkey.api.specimen.SpecimenRequestStatus; +import org.labkey.api.specimen.SpecimenSchema; +import org.labkey.api.specimen.Vial; +import org.labkey.api.specimen.importer.RequestabilityManager; +import org.labkey.api.specimen.importer.RollupHelper; +import org.labkey.api.specimen.importer.RollupInstance; +import org.labkey.api.specimen.importer.VialSpecimenRollup; +import org.labkey.api.specimen.security.permissions.RequestSpecimensPermission; +import org.labkey.api.specimen.settings.SettingsManager; +import org.labkey.api.study.QueryHelper; +import org.labkey.api.study.SpecimenUrls; +import org.labkey.api.study.Study; +import org.labkey.api.study.StudyService; +import org.labkey.api.study.StudyUtils; +import org.labkey.api.util.GUID; +import org.labkey.api.util.PageFlowUtil; +import org.labkey.api.util.Pair; +import org.labkey.api.view.ActionURL; +import org.labkey.specimen.model.SpecimenRequestEvent; +import org.labkey.specimen.requirements.SpecimenRequest; +import org.labkey.specimen.requirements.SpecimenRequestRequirement; +import org.labkey.specimen.requirements.SpecimenRequestRequirementProvider; +import org.labkey.specimen.security.permissions.ManageRequestsPermission; + +import java.io.IOException; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.locks.ReentrantLock; + +public class SpecimenRequestManager +{ + private static final SpecimenRequestManager INSTANCE = new SpecimenRequestManager(); + + private final QueryHelper _requestEventHelper; + private final QueryHelper _requestStatusHelper; + private final QueryHelper _requestHelper; + + public static SpecimenRequestManager get() + { + return INSTANCE; + } + + private SpecimenRequestManager() + { + _requestEventHelper = new QueryHelper<>(() -> SpecimenSchema.get().getTableInfoSampleRequestEvent(), SpecimenRequestEvent.class); + _requestStatusHelper = new QueryHelper<>(() -> SpecimenSchema.get().getTableInfoSampleRequestStatus(), SpecimenRequestStatus.class); + _requestHelper = new QueryHelper<>(() -> SpecimenSchema.get().getTableInfoSampleRequest(), SpecimenRequest.class); + + initGroupedValueAllowedColumnMap(); + } + + public List getRequestStatuses(Container c, User user) + { + List statuses = _requestStatusHelper.get(c, "SortOrder"); + // if the 'not-yet-submitted' status doesn't exist, create it here, with sort order -1, + // so it's always first. + if (statuses == null || statuses.isEmpty() || statuses.get(0).getSortOrder() != -1) + { + SpecimenRequestStatus notYetSubmittedStatus = new SpecimenRequestStatus(); + notYetSubmittedStatus.setContainer(c); + notYetSubmittedStatus.setFinalState(false); + notYetSubmittedStatus.setSpecimensLocked(true); + notYetSubmittedStatus.setLabel("Not Yet Submitted"); + notYetSubmittedStatus.setSortOrder(-1); + try (var ignore = SpringActionController.ignoreSqlUpdates()) + { + Table.insert(user, _requestStatusHelper.getTableInfo(), notYetSubmittedStatus); + } + statuses = _requestStatusHelper.get(c, "SortOrder"); + } + return statuses; + } + + public SpecimenRequestStatus getRequestShoppingCartStatus(Container c, User user) + { + List statuses = getRequestStatuses(c, user); + if (statuses.get(0).getSortOrder() != -1) + throw new IllegalStateException("Shopping cart status should be created automatically."); + return statuses.get(0); + } + + public SpecimenRequestStatus getInitialRequestStatus(Container c, User user, boolean nonCart) + { + List statuses = getRequestStatuses(c, user); + if (!nonCart && SettingsManager.get().isSpecimenShoppingCartEnabled(c)) + return statuses.get(0); + else + return statuses.get(1); + } + + public boolean hasEditRequestPermissions(User user, SpecimenRequest request) + { + if (request == null) + return false; + Container container = request.getContainer(); + if (!container.hasPermission(user, RequestSpecimensPermission.class)) + return false; + if (container.hasPermission(user, ManageRequestsPermission.class)) + return true; + + if (SettingsManager.get().isSpecimenShoppingCartEnabled(container)) + { + SpecimenRequestStatus cartStatus = getRequestShoppingCartStatus(container, user); + if (cartStatus.getRowId() == request.getStatusId() && request.getCreatedBy() == user.getUserId()) + return true; + } + return false; + } + + public Set getRequestStatusIdsInUse(Container c) + { + List requests = _requestHelper.get(c); + Set uniqueStatuses = new HashSet<>(); + for (SpecimenRequest request : requests) + uniqueStatuses.add(request.getStatusId()); + return uniqueStatuses; + } + + public List getRequestEvents(Container c) + { + return _requestEventHelper.get(c); + } + + public SpecimenRequestEvent getRequestEvent(Container c, int rowId) + { + return _requestEventHelper.get(c, rowId); + } + + public static class SpecimenRequestInput + { + private final String _title; + private final String _helpText; + private final int _displayOrder; + + private boolean _required; + private boolean _rememberSiteValue; + private boolean _multiLine; + private Map _locationToDefaultValue; + + public SpecimenRequestInput(String title, String helpText, int displayOrder, boolean multiLine, boolean required, boolean rememberSiteValue) + { + _title = title; + _required = required; + _rememberSiteValue = rememberSiteValue; + _helpText = helpText; + _displayOrder = displayOrder; + _multiLine = multiLine; + } + + public SpecimenRequestInput(String title, String helpText, int displayOrder) + { + this(title, helpText, displayOrder, false, false, false); + } + + public String getHelpText() + { + return _helpText; + } + + public boolean isRememberSiteValue() + { + return _rememberSiteValue; + } + + public boolean isRequired() + { + return _required; + } + + public String getTitle() + { + return _title; + } + + public int getDisplayOrder() + { + return _displayOrder; + } + + public boolean isMultiLine() + { + return _multiLine; + } + + public void setMultiLine(boolean multiLine) + { + _multiLine = multiLine; + } + + public void setRememberSiteValue(boolean rememberSiteValue) + { + _rememberSiteValue = rememberSiteValue; + } + + public void setRequired(boolean required) + { + _required = required; + } + + public Map getDefaultSiteValues(Container container) throws ValidationException + { + if (!isRememberSiteValue()) + throw new UnsupportedOperationException("Only those inputs set to remember site values can be queried for a site default."); + + if (_locationToDefaultValue != null) + return _locationToDefaultValue; + String defaultObjectLsid = getRequestInputDefaultObjectLsid(container); + String setItemLsid = ensureOntologyManagerSetItem(container, defaultObjectLsid, getTitle()); + Map locationToValue = new HashMap<>(); + + Map defaultValueProperties = OntologyManager.getPropertyObjects(container, setItemLsid); + if (defaultValueProperties != null) + { + for (Map.Entry defaultValue : defaultValueProperties.entrySet()) + { + String locationIdString = defaultValue.getKey().substring(defaultValue.getKey().lastIndexOf(".") + 1); + int locationId = Integer.parseInt(locationIdString); + locationToValue.put(locationId, defaultValue.getValue().getStringValue()); + } + } + _locationToDefaultValue = locationToValue; + return _locationToDefaultValue; + } + + public void setDefaultSiteValue(Container container, int locationId, String value) throws SQLException + { + try + { + assert locationId > 0 : "Invalid site id: " + locationId; + if (!isRememberSiteValue()) + throw new UnsupportedOperationException("Only those inputs configured to remember site values can set a site default."); + _locationToDefaultValue = null; + String parentObjectLsid = getRequestInputDefaultObjectLsid(container); + + String setItemLsid = ensureOntologyManagerSetItem(container, parentObjectLsid, getTitle()); + String propertyId = parentObjectLsid + "." + locationId; + ObjectProperty defaultValueProperty = new ObjectProperty(setItemLsid, container, propertyId, value); + OntologyManager.deleteProperty(setItemLsid, propertyId, container, container); + OntologyManager.insertProperties(container, setItemLsid, defaultValueProperty); + } + catch (ValidationException e) + { + throw new SQLException(e.getMessage()); + } + } + } + + public SpecimenRequestInput[] getNewSpecimenRequestInputs(Container container) throws SQLException + { + return getNewSpecimenRequestInputs(container, true); + } + + public SpecimenRequestInput[] getNewSpecimenRequestInputs(Container container, boolean createIfMissing) throws SQLException + { + String parentObjectLsid = getRequestInputObjectLsid(container); + Map resourceProperties = OntologyManager.getPropertyObjects(container, parentObjectLsid); + SpecimenRequestInput[] inputs = new SpecimenRequestInput[0]; + if (resourceProperties == null || resourceProperties.size() == 0) + { + if (createIfMissing) + { + try (var ignore = SpringActionController.ignoreSqlUpdates()) + { + inputs = new SpecimenRequestInput[] { + new SpecimenRequestInput("Assay Plan", "Please enter a description of or reference to the assay plan(s) that will be used for the requested specimens.", 0, true, true, false), + new SpecimenRequestInput("Shipping Information", "Please enter your shipping address along with any special instructions.", 1, true, true, true), + new SpecimenRequestInput("Comments", "Please enter any additional information regarding your request.", 2, true, false, false) + }; + saveNewSpecimenRequestInputs(container, inputs); + } + } + return inputs; + } + else + { + inputs = new SpecimenRequestInput[resourceProperties.size()]; + for (Map.Entry parentPropertyEntry : resourceProperties.entrySet()) + { + String resourcePropertyLsid = parentPropertyEntry.getKey(); + int displayOrder = Integer.parseInt(resourcePropertyLsid.substring(resourcePropertyLsid.lastIndexOf('.') + 1)); + + Map inputProperties = parentPropertyEntry.getValue().retrieveChildProperties(); + String title = inputProperties.get(parentObjectLsid + ".Title").getStringValue(); + String helpText = null; + if (inputProperties.get(parentObjectLsid + ".HelpText") != null) + helpText = inputProperties.get(parentObjectLsid + ".HelpText").getStringValue(); + boolean rememberSiteValue = inputProperties.get(parentObjectLsid + ".RememberSiteValue").getFloatValue() == 1; + boolean required = inputProperties.get(parentObjectLsid + ".Required").getFloatValue() == 1; + boolean multiLine = inputProperties.get(parentObjectLsid + ".MultiLine").getFloatValue() == 1; + inputs[displayOrder] = new SpecimenRequestInput(title, helpText, displayOrder, multiLine, required, rememberSiteValue); + } + } + return inputs; + } + + private static String getRequestInputObjectLsid(Container container) + { + return new Lsid(StudyService.SPECIMEN_NAMESPACE_PREFIX, "Folder-" + container.getRowId(), "RequestInput").toString(); + } + + private static String getRequestInputDefaultObjectLsid(Container container) + { + return new Lsid(StudyService.SPECIMEN_NAMESPACE_PREFIX, "Folder-" + container.getRowId(), "RequestInputDefault").toString(); + } + + private static String ensureOntologyManagerSetItem(Container container, String lsidBase, String uniqueItemId) throws ValidationException + { + try (var ignore = SpringActionController.ignoreSqlUpdates()) + { + Integer listParentObjectId = OntologyManager.ensureObject(container, lsidBase); + String listItemReferenceLsidPrefix = lsidBase + "#objectResource."; + String listItemObjectLsid = lsidBase + "#" + uniqueItemId; + String listItemPropertyReferenceLsid = listItemReferenceLsidPrefix + uniqueItemId; + + // ensure the object that corresponds to a single list item: + OntologyManager.ensureObject(container, listItemObjectLsid, listParentObjectId); + + // check to make sure that the list item is wired up to the top-level list object via a property: + Map properties = OntologyManager.getPropertyObjects(container, lsidBase); + if (!properties.containsKey(listItemPropertyReferenceLsid)) + { + // create the resource property that links the parent object to the list item object: + ObjectProperty resourceProperty = new ObjectProperty(lsidBase, container, + listItemPropertyReferenceLsid, listItemObjectLsid, PropertyType.RESOURCE); + OntologyManager.insertProperties(container, lsidBase, resourceProperty); + } + return listItemObjectLsid; + } + } + + public void saveNewSpecimenRequestInputs(Container container, SpecimenRequestInput[] inputs) throws SQLException + { + if (!requestInputsChanged(container, inputs)) + return; + + try { + String parentObjectLsid = getRequestInputObjectLsid(container); + String defaultValuesObjectLsid = getRequestInputDefaultObjectLsid(container); + OntologyManager.deleteOntologyObject(parentObjectLsid, container, true); + OntologyManager.deleteOntologyObject(defaultValuesObjectLsid, container, true); + for (int i = 0; i < inputs.length; i++) + { + SpecimenRequestInput input = inputs[i]; + String setItemLsid = ensureOntologyManagerSetItem(container, parentObjectLsid, "" + i); + ObjectProperty[] props = new ObjectProperty[5]; + props[0] = new ObjectProperty(setItemLsid, container, parentObjectLsid + ".HelpText", input.getHelpText()); + props[1] = new ObjectProperty(setItemLsid, container, parentObjectLsid + ".Required", input.isRequired() ? 1 : 0); + props[2] = new ObjectProperty(setItemLsid, container, parentObjectLsid + ".RememberSiteValue", input.isRememberSiteValue() ? 1 : 0); + props[3] = new ObjectProperty(setItemLsid, container, parentObjectLsid + ".Title", input.getTitle()); + props[4] = new ObjectProperty(setItemLsid, container, parentObjectLsid + ".MultiLine", input.isMultiLine() ? 1 : 0); + OntologyManager.insertProperties(container, setItemLsid, props); + } + } + catch (ValidationException ve) + { + throw new SQLException(ve.getMessage()); + } + } + + private boolean requestInputsChanged(Container container, SpecimenRequestInput[] newInputs) throws SQLException + { + SpecimenRequestInput[] oldInputs = getNewSpecimenRequestInputs(container, false); + if (oldInputs.length == 0) + return true; + else if (oldInputs.length != newInputs.length) + return true; + else + { + for (int i = 0; i < oldInputs.length; i++) + { + if (oldInputs[i].isMultiLine() != newInputs[i].isMultiLine() || + oldInputs[i].isRememberSiteValue() != newInputs[i].isRememberSiteValue() || + oldInputs[i].isRequired() != newInputs[i].isRequired() || + !oldInputs[i].getTitle().equals(newInputs[i].getTitle()) || + !getSafeString(oldInputs[i].getHelpText()).equals(getSafeString(newInputs[i].getHelpText()))) + return true; + } + } + return false; + } + + private String getSafeString(String str) + { + if (str == null) + return ""; + else + return str; + } + + public void createRequestStatus(User user, SpecimenRequestStatus status) + { + _requestStatusHelper.create(user, status); + } + + public void updateRequestStatus(User user, SpecimenRequestStatus status) + { + _requestStatusHelper.update(user, status); + } + + public void deleteRequestStatus(SpecimenRequestStatus status) + { + _requestStatusHelper.delete(status); + } + + public SpecimenRequestRequirement[] getRequestRequirements(SpecimenRequest request) + { + if (request == null) + return new SpecimenRequestRequirement[0]; + return request.getRequirements(); + } + + public void deleteRequestRequirement(User user, SpecimenRequestRequirement requirement) throws AttachmentService.DuplicateFilenameException + { + deleteRequestRequirement(user, requirement, true); + } + + public void deleteRequestRequirement(User user, SpecimenRequestRequirement requirement, boolean createEvent) throws AttachmentService.DuplicateFilenameException + { + if (createEvent) + createRequestEvent(user, requirement, RequestEventType.REQUIREMENT_REMOVED, requirement.getRequirementSummary(), null); + requirement.delete(); + } + + public void createRequestRequirement(User user, SpecimenRequestRequirement requirement, boolean createEvent) throws AttachmentService.DuplicateFilenameException + { + createRequestRequirement(user, requirement, createEvent, false); + } + + public void createRequestRequirement(User user, SpecimenRequestRequirement requirement, boolean createEvent, boolean force) throws AttachmentService.DuplicateFilenameException + { + SpecimenRequest request = getRequest(requirement.getContainer(), requirement.getRequestId()); + SpecimenRequestRequirement newRequirement = SpecimenRequestRequirementProvider.get().createRequirement(user, request, requirement, force); + if (newRequirement != null && createEvent) + createRequestEvent(user, requirement, RequestEventType.REQUIREMENT_ADDED, requirement.getRequirementSummary(), null); + } + + public void updateRequestRequirement(User user, SpecimenRequestRequirement requirement) + { + requirement.update(user); + } + + public boolean isInFinalState(SpecimenRequest request) + { + return getRequestStatus(request.getContainer(), request.getStatusId()).isFinalState(); + } + + public SpecimenRequestStatus getRequestStatus(Container c, int rowId) + { + return _requestStatusHelper.get(c, rowId); + } + + private SpecimenRequestEvent createRequestEvent(User user, SpecimenRequestEvent event) + { + return _requestEventHelper.create(user, event); + } + + public SpecimenRequestEvent createRequestEvent(User user, SpecimenRequestRequirement requirement, RequestEventType type, String comments, List attachments) throws AttachmentService.DuplicateFilenameException + { + return createRequestEvent(user, requirement.getContainer(), requirement.getRequestId(), requirement.getRowId(), type, comments, attachments); + } + + public SpecimenRequestEvent createRequestEvent(User user, SpecimenRequest request, RequestEventType type, String comments, List attachments) throws AttachmentService.DuplicateFilenameException + { + return createRequestEvent(user, request.getContainer(), request.getRowId(), -1, type, comments, attachments); + } + + private SpecimenRequestEvent createRequestEvent(User user, Container container, int requestId, int requirementId, RequestEventType type, String comments, List attachments) throws AttachmentService.DuplicateFilenameException + { + SpecimenRequestEvent event = new SpecimenRequestEvent(); + event.setEntryType(type.getDisplayText()); + event.setComments(comments); + event.setRequestId(requestId); + event.setCreated(new Date(System.currentTimeMillis())); + if (requirementId >= 0) + event.setRequirementId(requirementId); + event.setContainer(container); + event.setEntityId(GUID.makeGUID()); + event = createRequestEvent(user, event); + try + { + AttachmentService.get().addAttachments(event, attachments, user); + } + catch (AttachmentService.DuplicateFilenameException e) + { + // UI should (minimally) catch and display these errors nicely + throw e; + } + catch (IOException e) + { + // this is unexpected, and indicative of a larger system problem; we'll convert to a runtime + // exception, rather than requiring all event loggers to handle this unlikely scenario: + throw new RuntimeException(e); + } + return event; + } + + public List getRequests(Container c, User user) + { + SimpleFilter filter = new SimpleFilter(FieldKey.fromParts("Hidden"), Boolean.FALSE); + if (user != null) + filter.addCondition(FieldKey.fromParts("CreatedBy"), user.getUserId()); + return _requestHelper.get(c, filter, "-Created"); + } + + public SpecimenRequest getRequest(Container c, int rowId) + { + return _requestHelper.get(c, rowId); + } + + public SpecimenRequest createRequest(User user, SpecimenRequest request, boolean createEvent) throws AttachmentService.DuplicateFilenameException + { + request = _requestHelper.create(user, request); + if (createEvent) + createRequestEvent(user, request, RequestEventType.REQUEST_CREATED, request.getRequestDescription(), null); + return request; + } + + public void updateRequest(User user, SpecimenRequest request) throws RequestabilityManager.InvalidRuleException + { + DbScope scope = SpecimenSchema.get().getScope(); + try (DbScope.Transaction transaction = scope.ensureTransaction()) + { + _requestHelper.update(user, request); + + // update specimen states + List vials = request.getVials(); + if (vials.size() > 0) + { + SpecimenRequestStatus status = getRequestStatus(request.getContainer(), request.getStatusId()); + updateSpecimenStatus(vials, user, status.isSpecimensLocked()); + } + transaction.commit(); + } + } + + private static final ReentrantLock REQUEST_ADDITION_LOCK = new ReentrantLock(); + + public void createRequestSpecimenMapping(User user, SpecimenRequest request, List vials, boolean createEvents, boolean createRequirements) + throws RequestabilityManager.InvalidRuleException, AttachmentService.DuplicateFilenameException, SpecimenRequestException + { + if (vials == null || vials.size() == 0) + return; + + DbScope scope = SpecimenSchema.get().getScope(); + try (DbScope.Transaction transaction = scope.ensureTransaction(REQUEST_ADDITION_LOCK)) + { + for (Vial vial : vials) + { + if (!request.getContainer().getId().equals(vial.getContainer().getId())) + throw new IllegalStateException("Mismatched containers."); + + if (!vial.isAvailable()) + throw new SpecimenRequestException("Vial unavailable."); + } + + for (Vial vial : vials) + { + Map fields = new HashMap<>(); + fields.put("Container", request.getContainer().getId()); + fields.put("SampleRequestId", request.getRowId()); + fields.put("SpecimenGlobalUniqueId", vial.getGlobalUniqueId()); + Table.insert(user, SpecimenSchema.get().getTableInfoSampleRequestSpecimen(), fields); + if (createEvents) + SpecimenRequestManager.get().createRequestEvent(user, request, RequestEventType.SPECIMEN_ADDED, vial.getSpecimenDescription(), null); + } + + if (createRequirements) + SpecimenRequestRequirementProvider.get().generateDefaultRequirements(user, request); + + SpecimenRequestStatus status = getRequestStatus(request.getContainer(), request.getStatusId()); + updateSpecimenStatus(vials, user, status.isSpecimensLocked()); + + transaction.commit(); + } + } + + /** + * Update the lockedInRequest and available field states for the set of specimens. + */ + private void updateSpecimenStatus(List vials, User user, boolean lockedInRequest) throws RequestabilityManager.InvalidRuleException + { + for (Vial vial : vials) + { + vial.setLockedInRequest(lockedInRequest); + Table.update(user, SpecimenSchema.get().getTableInfoVial(vial.getContainer()), vial.getRowMap(), vial.getRowId()); + } + updateRequestabilityAndCounts(vials, user); + if (vials.size() > 0) + clearCaches(getContainer(vials)); + } + + private void updateRequestabilityAndCounts(List vials, User user) throws RequestabilityManager.InvalidRuleException + { + if (vials.size() == 0) + return; + Container container = getContainer(vials); + + // update requestable flags before updating counts, since available count could change: + for (int start = 0; start < vials.size(); start += 1000) + { + List subset = vials.subList(start, start + Math.min(1000, vials.size() - start)); + RequestabilityManager.getInstance().updateRequestability(container, user, subset); + } + + for (int start = 0; start < vials.size(); start += 1000) + { + List subset = vials.subList(start, start + Math.min(1000, vials.size() - start)); + updateVialCounts(container, user, subset); + } + } + + private Container getContainer(List vials) + { + Container container = vials.get(0).getContainer(); + if (AppProps.getInstance().isDevMode()) + { + for (int i = 1; i < vials.size(); i++) + { + if (!container.equals(vials.get(i).getContainer())) + throw new IllegalStateException("All specimens must be from the same container"); + } + } + return container; + } + + private static final String UPDATE_SPECIMEN_SETS = + " SET\n" + + " TotalVolume = VialCounts.TotalVolume,\n" + + " AvailableVolume = VialCounts.AvailableVolume,\n" + + " VialCount = VialCounts.VialCount,\n" + + " LockedInRequestCount = VialCounts.LockedInRequestCount,\n" + + " AtRepositoryCount = VialCounts.AtRepositoryCount,\n" + + " AvailableCount = VialCounts.AvailableCount,\n" + + " ExpectedAvailableCount = VialCounts.ExpectedAvailableCount"; + + private static final String UPDATE_SPECIMEN_SELECTS = + "\nFROM (\n" + + "\tSELECT SpecimenId,\n" + + "\t\tSUM(Volume) AS TotalVolume,\n" + + "\t\tSUM(CASE Available WHEN ? THEN Volume ELSE 0 END) AS AvailableVolume,\n" + + "\t\tCOUNT(GlobalUniqueId) AS VialCount,\n" + + "\t\tSUM(CASE LockedInRequest WHEN ? THEN 1 ELSE 0 END) AS LockedInRequestCount,\n" + + "\t\tSUM(CASE AtRepository WHEN ? THEN 1 ELSE 0 END) AS AtRepositoryCount,\n" + + "\t\tSUM(CASE Available WHEN ? THEN 1 ELSE 0 END) AS AvailableCount,\n" + + "\t\t(COUNT(GlobalUniqueId) - SUM(\n" + + "\t\tCASE\n" + + "\t\t\t(CASE LockedInRequest WHEN ? THEN 1 ELSE 0 END) -- Null is considered false for LockedInRequest\n" + + "\t\t\t| (CASE Requestable WHEN ? THEN 1 ELSE 0 END)-- Null is considered true for Requestable\n" + + "\t\t\tWHEN 1 THEN 1 ELSE 0 END)\n" + + "\t\t) AS ExpectedAvailableCount"; + + // "\tFROM "; + + private void updateVialCounts(Container container, User user, List vials) + { + TableInfo tableInfoSpecimen = SpecimenSchema.get().getTableInfoSpecimen(container); + TableInfo tableInfoVial = SpecimenSchema.get().getTableInfoVial(container); + + String tableInfoSpecimenSelectName = tableInfoSpecimen.getSelectName(); + String tableInfoVialSelectName = tableInfoVial.getSelectName(); + RollupHelper.RollupMap matchedRollups = RollupHelper.getVialToSpecimenRollups(container, user); + + SQLFragment updateSql = new SQLFragment(); + updateSql.append("UPDATE ").append(tableInfoSpecimenSelectName).append(UPDATE_SPECIMEN_SETS); + for (List> rollupList : matchedRollups.values()) + for (Pair rollupItem : rollupList) + { + ColumnInfo column = tableInfoSpecimen.getColumn(rollupItem.first); + if (null == column) + throw new IllegalStateException("Expected Specimen table column to exist."); + String colSelectName = column.getSelectName(); + updateSql.append(",\n ").append(colSelectName).append(" = VialCounts.").append(colSelectName); + } + + updateSql.append(UPDATE_SPECIMEN_SELECTS); + updateSql.add(Boolean.TRUE); // AvailableVolume + updateSql.add(Boolean.TRUE); // LockedInRequestCount + updateSql.add(Boolean.TRUE); // AtRepositoryCount + updateSql.add(Boolean.TRUE); // AvailableCount + updateSql.add(Boolean.TRUE); // LockedInRequest case of ExpectedAvailableCount + updateSql.add(Boolean.FALSE); // Requestable case of ExpectedAvailableCount + + for (Map.Entry>> entry : matchedRollups.entrySet()) + { + ColumnInfo vialColumn = tableInfoVial.getColumn(entry.getKey()); + if (null == vialColumn) + throw new IllegalStateException("Expected Vial table column to exist."); + String fromName = vialColumn.getSelectName(); + for (RollupInstance rollupItem : entry.getValue()) + { + VialSpecimenRollup rollup = rollupItem.second; + ColumnInfo column = tableInfoSpecimen.getColumn(rollupItem.first); + if (null == column) + throw new IllegalStateException("Expected Specimen table column to exist."); + String toName = column.getSelectName(); + + updateSql.append(",\n\t\t").append(rollup.getRollupSql(fromName, toName)); + } + } + + updateSql.append("\tFROM ").append(tableInfoVialSelectName).append("\n"); + + if (vials != null && vials.size() > 0) + { + Set specimenIds = new HashSet<>(); + for (Vial vial : vials) + specimenIds.add(vial.getSpecimenId()); + + updateSql.append("WHERE ") + .append(tableInfoVial.getColumn("SpecimenId").getValueSql(tableInfoVialSelectName)) + .append(" IN ("); + String sep = ""; + for (Long id : specimenIds) + { + updateSql.append(sep).append("?"); + updateSql.add(id); + sep = ", "; + } + updateSql.append(")\n"); + } + + updateSql.append("\tGROUP BY SpecimenId\n) VialCounts\nWHERE ") + .append(tableInfoSpecimen.getColumn("RowId").getValueSql(tableInfoSpecimenSelectName)) + .append("= VialCounts.SpecimenId"); + new SqlExecutor(SpecimenSchema.get().getSchema()).execute(updateSql); + } + + public void updateVialCounts(Container container, User user) + { + updateVialCounts(container, user, null); + } + + public void clearCaches(Container c) + { + _requestEventHelper.clearCache(c); + _requestHelper.clearCache(c); + _requestStatusHelper.clearCache(c); + for (Study study : StudyService.get().getAncillaryStudies(c)) + clearCaches(study.getContainer()); + + clearGroupedValuesForColumn(c); + } + + private static class GroupedValueColumnHelper + { + private final String _viewColumnName; + private final String _sqlColumnName; + private final String _urlFilterName; + private final String _joinColumnName; + + public GroupedValueColumnHelper(String sqlColumnName, String viewColumnName, String urlFilterName, String joinColumnName) + { + _sqlColumnName = sqlColumnName; + _viewColumnName = viewColumnName; + _urlFilterName = urlFilterName; + _joinColumnName = joinColumnName; + } + + public String getViewColumnName() + { + return _viewColumnName; + } + + public String getSqlColumnName() + { + return _sqlColumnName; + } + + public String getUrlFilterName() + { + return _urlFilterName; + } + + public String getJoinColumnName() + { + return _joinColumnName; + } + + public FieldKey getFieldKey() + { + // constructs FieldKey whether it needs join or not + if (null == _joinColumnName) + return FieldKey.fromString(_viewColumnName); + return FieldKey.fromParts(_sqlColumnName, _joinColumnName); + } + } + + // Map "ViewColumnName" name to object with sql column name and url filter name + private final Map _groupedValueAllowedColumnMap = new HashMap<>(); + + private void initGroupedValueAllowedColumnMap() + { // sqlColumnName viewColumnName urlFilterName joinColumnName + _groupedValueAllowedColumnMap.put("Primary Type", new GroupedValueColumnHelper("PrimaryTypeId", "PrimaryType", "PrimaryType/Description", "PrimaryType")); + _groupedValueAllowedColumnMap.put("Derivative Type", new GroupedValueColumnHelper("DerivativeTypeId", "DerivativeType", "DerivativeType/Description", "Derivative")); + _groupedValueAllowedColumnMap.put("Additive Type", new GroupedValueColumnHelper("AdditiveTypeId", "AdditiveType", "AdditiveType/Description", "Additive")); + _groupedValueAllowedColumnMap.put("Derivative Type2", new GroupedValueColumnHelper("DerivativeTypeId2", "DerivativeType2", "DerivativeType2/Description", "Derivative")); + _groupedValueAllowedColumnMap.put("Sub Additive Derivative",new GroupedValueColumnHelper("SubAdditiveDerivative", "SubAdditiveDerivative", "SubAdditiveDerivative", null)); + _groupedValueAllowedColumnMap.put("Clinic", new GroupedValueColumnHelper("originatinglocationid", "Clinic", "Clinic/Label", "Label")); + _groupedValueAllowedColumnMap.put("Processing Location", new GroupedValueColumnHelper("ProcessingLocation", "ProcessingLocation", "ProcessingLocation/Label", "Label")); + _groupedValueAllowedColumnMap.put("Protocol Number", new GroupedValueColumnHelper("ProtocolNumber", "ProtocolNumber", "ProtocolNumber", null)); + _groupedValueAllowedColumnMap.put("Tube Type", new GroupedValueColumnHelper("TubeType", "TubeType", "TubeType", null)); + _groupedValueAllowedColumnMap.put("Site Name", new GroupedValueColumnHelper("CurrentLocation", "SiteName", "SiteName/Label", "Label")); + _groupedValueAllowedColumnMap.put("Available", new GroupedValueColumnHelper("Available", "Available", "Available", null)); + _groupedValueAllowedColumnMap.put("Freezer", new GroupedValueColumnHelper("Freezer", "Freezer", "Freezer", null)); + _groupedValueAllowedColumnMap.put("Fr Container", new GroupedValueColumnHelper("Fr_Container", "Fr_Container", "Fr_Container", null)); + _groupedValueAllowedColumnMap.put("Fr Position", new GroupedValueColumnHelper("Fr_Position", "Fr_Position", "Fr_Position", null)); + _groupedValueAllowedColumnMap.put("Fr Level1", new GroupedValueColumnHelper("Fr_Level1", "Fr_Level1", "Fr_Level1", null)); + _groupedValueAllowedColumnMap.put("Fr Level2", new GroupedValueColumnHelper("Fr_Level2", "Fr_Level2", "Fr_Level2", null)); + } + + public Map getGroupedValueAllowedMap() + { + return _groupedValueAllowedColumnMap; + } + + public String[] getGroupedValueAllowedColumns() + { + Set keySet = _groupedValueAllowedColumnMap.keySet(); + String[] allowedColumns = keySet.toArray(new String[keySet.size()]); + Arrays.sort(allowedColumns, new ComparableComparator<>()); + return allowedColumns; + } + + private static class GroupedValueFilter + { + private String _viewColumnName; + private String _filterValueName; + + public GroupedValueFilter() + { + } + + public String getViewColumnName() + { + return _viewColumnName; + } + + public String getFilterValueName() + { + return _filterValueName; + } + + public void setFilterValueName(String filterValueName) + { + _filterValueName = filterValueName; + } + + public void setViewColumnName(String viewColumnName) + { + _viewColumnName = viewColumnName; + } + } + + private DatabaseCache>> _groupedValuesCache = null; + + private static class GroupedResults + { + public String viewName; + public String urlFilterName; + public String labelValue; + public long count; + public Map childGroupedResultsMap; + } + + private static String getGroupedValuesCacheKey(Container container) + { + return container.getId(); + } + + public void clearGroupedValuesForColumn(Container container) + { + if (null == _groupedValuesCache) + return; + + String cacheKey = getGroupedValuesCacheKey(container); + _groupedValuesCache.remove(cacheKey); + } + + @NotNull + public Map> getGroupedValuesForColumn(Container container, User user, ArrayList groupings) + { + // ColumnName and filter names are "QueryView" names; map them to actual table names before building query + Map> groupedValues = Collections.emptyMap(); + Study study = StudyService.get().getStudy(container); + if (study == null) + return groupedValues; + + UserSchema schema = SpecimenQuerySchema.get(study, user); + TableInfo tableInfo = schema.getTable(SpecimenQuerySchema.SPECIMEN_WRAP_TABLE_NAME); + String cacheKey = getGroupedValuesCacheKey(container); + if (null != _groupedValuesCache) + { + groupedValues = _groupedValuesCache.get(cacheKey); + if (null != groupedValues) + return groupedValues; + } + else + { + _groupedValuesCache = new DatabaseCache<>( + SpecimenSchema.get().getScope(), 10, 8 * CacheManager.HOUR, "Grouped Values Cache"); + } + + try + { + groupedValues = new HashMap<>(); + QueryService queryService = QueryService.get(); + for (String[] grouping : groupings) + { + List fieldKeys = new ArrayList<>(); + for (String aGrouping : grouping) + { + if (!StringUtils.isNotBlank(aGrouping)) + break; // Grouping may have null/blank entries for groupBys that are not chosen to be used + GroupedValueColumnHelper columnHelper = getGroupedValueAllowedMap().get(aGrouping); + FieldKey fieldKey = columnHelper.getFieldKey(); + fieldKeys.add(fieldKey); + } + + if (fieldKeys.isEmpty()) + continue; // Nothing specified for grouping + + // Basic SQL with joins + Map columnMap = queryService.getColumns(tableInfo, fieldKeys); + + SQLFragment sql = queryService.getSelectSQL(tableInfo, columnMap.values(), null, null, -1, 0, false); + + // Insert COUNT + String sampleCountName = tableInfo.getSqlDialect().makeLegalIdentifier("SampleCount"); + String countStr = " COUNT(*) As " + sampleCountName + ",\n"; + int insertIndex = sql.indexOf("SELECT"); + sql.insert(insertIndex + 6, countStr); + + sql.append("GROUP BY "); + boolean firstGroupBy = true; + for (ColumnInfo columnInfo : columnMap.values()) + { + if (!firstGroupBy) + sql.append(", "); + firstGroupBy = false; + sql.append(columnInfo.getValueSql(tableInfo.getTitle())); + } + + sql.append("\nORDER BY "); + boolean firstOrderBy = true; + for (ColumnInfo columnInfo : columnMap.values()) + { + if (!firstOrderBy) + sql.append(", "); + firstOrderBy = false; + sql.append(columnInfo.getValueSql(tableInfo.getTitle())); + } + + SqlSelector selector = new SqlSelector(tableInfo.getSchema(), sql); + + try (TableResultSet resultSet = selector.getResultSet()) + { + if (null != resultSet) + { + // The result set is grouped by all levels together, so at the upper levels, we have to group ourselves + // Build a tree of GroupedResultsMaps, one level for each grouping level + // + Map groupedResultsMap = new HashMap<>(); + while (resultSet.next()) + { + Map rowMap = resultSet.getRowMap(); + long count = 0; + Object countObject = rowMap.get(sampleCountName); + if (countObject instanceof Long) + count = (Long)countObject; + else if (countObject instanceof Integer) + count = (Integer)countObject; + + Map currentGroupedResultsMap = groupedResultsMap; + + for (String s : grouping) + { + if (!StringUtils.isNotBlank(s)) + break; // Grouping may have null entries for groupBys that are not chosen to be used + + GroupedValueColumnHelper columnHelper = getGroupedValueAllowedMap().get(s); + ColumnInfo columnInfo = columnMap.get(columnHelper.getFieldKey()); + Object value = rowMap.get(columnInfo.getAlias()); + String labelValue = (null != value) ? value.toString() : null; + GroupedResults groupedResults = currentGroupedResultsMap.get(labelValue); + if (null == groupedResults) + { + groupedResults = new GroupedResults(); + groupedResults.viewName = s; + groupedResults.urlFilterName = columnHelper.getUrlFilterName(); + groupedResults.labelValue = labelValue; + groupedResults.childGroupedResultsMap = new HashMap<>(); + currentGroupedResultsMap.put(labelValue, groupedResults); + } + groupedResults.count += count; + currentGroupedResultsMap = groupedResults.childGroupedResultsMap; + } + } + + Map groupedValue; + if (!groupedResultsMap.isEmpty()) + { + groupedValue = buildGroupedValue(groupedResultsMap, container, new ArrayList<>()); + } + else + { + groupedValue = new HashMap<>(2); + groupedValue.put("name", grouping[0]); + groupedValue.put("values", new ArrayList>()); + } + groupedValues.put(grouping[0], groupedValue); + } + } + } + + _groupedValuesCache.put(cacheKey, groupedValues); + } + catch (SQLException e) + { + throw new RuntimeSQLException(e); + } + + return groupedValues; + } + + private Map buildGroupedValue(Map groupedResultsMap, Container container, List groupedValueFilters) + { + String viewName = null; + ArrayList> groupedValues = new ArrayList<>(); + for (GroupedResults groupedResults : groupedResultsMap.values()) + { + viewName = groupedResults.viewName; // They are all the same in this collection + Map groupedValue = new HashMap<>(5); + groupedValue.put("label", (null != groupedResults.labelValue) ? groupedResults.labelValue : "[empty]"); + groupedValue.put("count", groupedResults.count); + groupedValue.put("url", getURL(container, groupedResults.urlFilterName, groupedValueFilters, groupedResults.labelValue)); + Map childGroupResultsMap = groupedResults.childGroupedResultsMap; + if (null != childGroupResultsMap && !childGroupResultsMap.isEmpty()) + { + GroupedValueFilter groupedValueFilter = new GroupedValueFilter(); + groupedValueFilter.setViewColumnName(groupedResults.viewName); + groupedValueFilter.setFilterValueName(null != groupedResults.labelValue ? groupedResults.labelValue : null); + List groupedValueFiltersCopy = new ArrayList<>(groupedValueFilters); // Need copy because can't share across members of groupedResultsMap + groupedValueFiltersCopy.add(groupedValueFilter); + Map nextLevelGroup = buildGroupedValue(childGroupResultsMap, container, groupedValueFiltersCopy); + groupedValue.put("group", nextLevelGroup); + + } + groupedValues.add(groupedValue); + } + + groupedValues.sort((o, o1) -> + { + String str = (String) o.get("label"); + String str1 = (String) o1.get("label"); + if (null == str) + { + if (null == str1) + return 0; + else + return 1; + } + else if (null == str1) + return -1; + return (str.compareTo(str1)); + }); + + Map groupedValue = new HashMap<>(2); + groupedValue.put("name", viewName); + groupedValue.put("values", groupedValues); + return groupedValue; + } + + private ActionURL getURL(Container container, String groupColumnName, List filterNamesAndValues, String label) + { + ActionURL url = PageFlowUtil.urlProvider(SpecimenUrls.class).getSpecimensURL(container, true); + addFilterParameter(url, groupColumnName, label); + for (GroupedValueFilter filterColumnAndValue : filterNamesAndValues) + addFilterParameter(url, getGroupedValueAllowedMap().get(filterColumnAndValue.getViewColumnName()).getUrlFilterName(), filterColumnAndValue.getFilterValueName()); + return url; + } + + private void addFilterParameter(ActionURL url, String urlColumnName, String label) + { + url.addParameter("SpecimenDetail." + urlColumnName + "~eq", label); + } + + public void deleteRequest(User user, SpecimenRequest request) throws RequestabilityManager.InvalidRuleException, AttachmentService.DuplicateFilenameException + { + DbScope scope = SpecimenSchema.get().getScope(); + try (DbScope.Transaction transaction = scope.ensureTransaction()) + { + List vials = request.getVials(); + List specimenIds = new ArrayList<>(vials.size()); + for (Vial vial : vials) + specimenIds.add(vial.getRowId()); + + deleteRequestSpecimenMappings(user, request, specimenIds, false); + + deleteMissingSpecimens(request); + + SpecimenRequestRequirementProvider.get().deleteRequirements(request); + + deleteRequestEvents(request); + _requestHelper.delete(request); + + transaction.commit(); + } + } + + public void deleteRequestSpecimenMappings(User user, SpecimenRequest request, List vialIds, boolean createEvents) + throws RequestabilityManager.InvalidRuleException, AttachmentService.DuplicateFilenameException + { + if (vialIds == null || vialIds.size() == 0) + return; + + Set vialRowIds = new HashSet<>(vialIds); + List vials = SpecimenManagerNew.get().getVials(request.getContainer(), user, vialRowIds); + List globalUniqueIds = new ArrayList<>(vials.size()); + List descriptions = new ArrayList<>(); + for (Vial vial : vials) + { + globalUniqueIds.add(vial.getGlobalUniqueId()); + descriptions.add(vial.getSpecimenDescription()); + } + + DbScope scope = SpecimenSchema.get().getScope(); + try (DbScope.Transaction transaction = scope.ensureTransaction()) + { + SimpleFilter filter = SimpleFilter.createContainerFilter(request.getContainer()); + filter.addCondition(FieldKey.fromParts("SampleRequestId"), request.getRowId()); + filter.addInClause(FieldKey.fromParts("SpecimenGlobalUniqueId"), globalUniqueIds); + Table.delete(SpecimenSchema.get().getTableInfoSampleRequestSpecimen(), filter); + if (createEvents) + { + for (String description : descriptions) + SpecimenRequestManager.get().createRequestEvent(user, request, RequestEventType.SPECIMEN_REMOVED, description, null); + } + + updateSpecimenStatus(vials, user, false); + + transaction.commit(); + } + } + + public void deleteMissingSpecimens(SpecimenRequest specimenRequest) + { + List missingSpecimens = getMissingSpecimens(specimenRequest); + if (missingSpecimens.isEmpty()) + return; + SimpleFilter filter = SimpleFilter.createContainerFilter(specimenRequest.getContainer()); + filter.addCondition(FieldKey.fromParts("SampleRequestId"), specimenRequest.getRowId()); + filter.addInClause(FieldKey.fromParts("SpecimenGlobalUniqueId"), missingSpecimens); + Table.delete(SpecimenSchema.get().getTableInfoSampleRequestSpecimen(), filter); + } + + public List getMissingSpecimens(SpecimenRequest specimenRequest) + { + Container container = specimenRequest.getContainer(); + TableInfo tableInfoVial = SpecimenSchema.get().getTableInfoVial(container); + SQLFragment sql = new SQLFragment("SELECT SpecimenGlobalUniqueId FROM study.SampleRequestSpecimen WHERE SampleRequestId = ? and Container = ? and \n" + + "SpecimenGlobalUniqueId NOT IN (SELECT "); + sql.add(specimenRequest.getRowId()); + sql.add(container); + sql.append(tableInfoVial.getColumn("GlobalUniqueId").getValueSql("Vial")) + .append(" FROM ").append(tableInfoVial.getFromSQL("Vial")).append(")"); + + return new SqlSelector(SpecimenSchema.get().getSchema(), sql).getArrayList(String.class); + } + + public @NotNull List getRequestIdsForSpecimen(Vial vial) + { + return getRequestIdsForSpecimen(vial, false); + } + + public @NotNull List getRequestIdsForSpecimen(Vial vial, boolean lockingRequestsOnly) + { + if (vial == null) + return Collections.emptyList(); + + SQLFragment sql = new SQLFragment("SELECT SampleRequestId FROM " + SpecimenSchema.get().getTableInfoSampleRequestSpecimen() + + " Map, " + SpecimenSchema.get().getTableInfoSampleRequest() + " Request, " + + SpecimenSchema.get().getTableInfoSampleRequestStatus() + " Status WHERE SpecimenGlobalUniqueId = ? " + + "AND Request.Container = ? AND Map.Container = Request.Container AND Status.Container = Request.Container " + + "AND Map.SampleRequestId = Request.RowId AND Request.StatusId = Status.RowId"); + sql.add(vial.getGlobalUniqueId()); + sql.add(vial.getContainer().getId()); + + if (lockingRequestsOnly) + { + sql.append(" AND Status.SpecimensLocked = ?"); + sql.add(Boolean.TRUE); + } + + return new SqlSelector(SpecimenSchema.get().getSchema(), sql).getArrayList(Integer.class); + } + + private void deleteRequestEvents(SpecimenRequest request) + { + SimpleFilter filter = new SimpleFilter(FieldKey.fromParts("RequestId"), request.getRowId()); + List events = _requestEventHelper.get(request.getContainer(), filter); + for (SpecimenRequestEvent event : events) + { + AttachmentService.get().deleteAttachments(event); + _requestEventHelper.delete(event); + } + } + + public RequestedSpecimens getRequestableBySpecimenHash(Container c, User user, Set formValues, Integer preferredLocation) throws AmbiguousLocationException + { + Map> vialsByHash = SpecimenManagerNew.get().getVialsForSpecimenHashes(c, user, formValues, true); + + if (vialsByHash == null || vialsByHash.isEmpty()) + return new RequestedSpecimens(Collections.emptyList()); + + if (preferredLocation == null) + { + Collection preferredLocations = StudyUtils.getPreferredProvidingLocations(vialsByHash.values()); + if (preferredLocations.size() == 1) + preferredLocation = preferredLocations.iterator().next(); + else if (preferredLocations.size() > 1) + throw new AmbiguousLocationException(c, preferredLocations); + } + + List requestedSpecimens = new ArrayList<>(vialsByHash.size()); + Set providingLocations = new HashSet<>(); + + for (List vials : vialsByHash.values()) + { + Vial selectedVial = null; + if (preferredLocation == null) + selectedVial = vials.get(0); + else + { + for (Iterator it = vials.iterator(); it.hasNext() && selectedVial == null;) + { + Vial vial = it.next(); + if (vial.getCurrentLocation() != null && vial.getCurrentLocation().intValue() == preferredLocation.intValue()) + selectedVial = vial; + } + } + if (selectedVial == null) + throw new IllegalStateException("Vial was not available from specified location " + preferredLocation); + providingLocations.add(selectedVial.getCurrentLocation()); + requestedSpecimens.add(selectedVial); + } + + return new RequestedSpecimens(requestedSpecimens, providingLocations); + } +} + diff --git a/specimen/src/org/labkey/specimen/actions/ManageReqsBean.java b/specimen/src/org/labkey/specimen/actions/ManageReqsBean.java index 7e47f832..49b627bc 100644 --- a/specimen/src/org/labkey/specimen/actions/ManageReqsBean.java +++ b/specimen/src/org/labkey/specimen/actions/ManageReqsBean.java @@ -2,11 +2,11 @@ import org.labkey.api.data.Container; import org.labkey.api.security.User; -import org.labkey.api.specimen.model.SpecimenRequestActor; -import org.labkey.api.specimen.requirements.RequirementProvider; -import org.labkey.api.specimen.requirements.SpecimenRequestRequirement; -import org.labkey.api.specimen.requirements.SpecimenRequestRequirementProvider; -import org.labkey.api.specimen.requirements.SpecimenRequestRequirementType; +import org.labkey.specimen.model.SpecimenRequestActor; +import org.labkey.specimen.requirements.RequirementProvider; +import org.labkey.specimen.requirements.SpecimenRequestRequirement; +import org.labkey.specimen.requirements.SpecimenRequestRequirementProvider; +import org.labkey.specimen.requirements.SpecimenRequestRequirementType; public class ManageReqsBean { diff --git a/specimen/src/org/labkey/specimen/actions/ManageRequestBean.java b/specimen/src/org/labkey/specimen/actions/ManageRequestBean.java index b7dd56ce..0488046b 100644 --- a/specimen/src/org/labkey/specimen/actions/ManageRequestBean.java +++ b/specimen/src/org/labkey/specimen/actions/ManageRequestBean.java @@ -3,25 +3,25 @@ import org.labkey.api.data.ActionButton; import org.labkey.api.data.Container; import org.labkey.api.data.MenuButton; -import org.labkey.api.specimen.SpecimenRequestManager; import org.labkey.api.specimen.SpecimenRequestStatus; import org.labkey.api.specimen.Vial; import org.labkey.api.specimen.location.LocationImpl; import org.labkey.api.specimen.location.LocationManager; -import org.labkey.api.specimen.model.SpecimenRequestActor; -import org.labkey.specimen.notifications.ActorNotificationRecipientSet; -import org.labkey.api.specimen.requirements.SpecimenRequest; -import org.labkey.api.specimen.requirements.SpecimenRequestRequirement; -import org.labkey.api.specimen.requirements.SpecimenRequestRequirementProvider; -import org.labkey.api.specimen.security.permissions.ManageRequestsPermission; import org.labkey.api.study.Location; import org.labkey.api.util.Button; import org.labkey.api.view.ActionURL; import org.labkey.api.view.DisplayElement; import org.labkey.api.view.ViewContext; +import org.labkey.specimen.SpecimenRequestManager; import org.labkey.specimen.actions.SpecimenController.ImportVialIdsAction; import org.labkey.specimen.actions.SpecimenController.OverviewAction; import org.labkey.specimen.actions.SpecimenController.RemoveRequestSpecimensAction; +import org.labkey.specimen.model.SpecimenRequestActor; +import org.labkey.specimen.notifications.ActorNotificationRecipientSet; +import org.labkey.specimen.requirements.SpecimenRequest; +import org.labkey.specimen.requirements.SpecimenRequestRequirement; +import org.labkey.specimen.requirements.SpecimenRequestRequirementProvider; +import org.labkey.specimen.security.permissions.ManageRequestsPermission; import java.util.ArrayList; import java.util.HashMap; diff --git a/specimen/src/org/labkey/specimen/actions/ManageRequestInputsBean.java b/specimen/src/org/labkey/specimen/actions/ManageRequestInputsBean.java index 434e2ee3..a29b36e0 100644 --- a/specimen/src/org/labkey/specimen/actions/ManageRequestInputsBean.java +++ b/specimen/src/org/labkey/specimen/actions/ManageRequestInputsBean.java @@ -1,8 +1,8 @@ package org.labkey.specimen.actions; import org.labkey.api.data.Container; -import org.labkey.api.specimen.SpecimenRequestManager; import org.labkey.api.view.ViewContext; +import org.labkey.specimen.SpecimenRequestManager; import java.sql.SQLException; diff --git a/specimen/src/org/labkey/specimen/actions/ShowGroupMembersAction.java b/specimen/src/org/labkey/specimen/actions/ShowGroupMembersAction.java index 3eaef996..bbb1b2bc 100644 --- a/specimen/src/org/labkey/specimen/actions/ShowGroupMembersAction.java +++ b/specimen/src/org/labkey/specimen/actions/ShowGroupMembersAction.java @@ -29,9 +29,6 @@ import org.labkey.api.security.permissions.UserManagementPermission; import org.labkey.api.specimen.location.LocationImpl; import org.labkey.api.specimen.location.LocationManager; -import org.labkey.api.specimen.model.SpecimenRequestActor; -import org.labkey.api.specimen.requirements.SpecimenRequestRequirementProvider; -import org.labkey.specimen.security.permissions.ManageSpecimenActorsPermission; import org.labkey.api.study.StudyUrls; import org.labkey.api.util.PageFlowUtil; import org.labkey.api.view.ActionURL; @@ -39,6 +36,9 @@ import org.labkey.api.view.NavTree; import org.labkey.api.view.NotFoundException; import org.labkey.specimen.actions.SpecimenController.ManageActorsAction; +import org.labkey.specimen.model.SpecimenRequestActor; +import org.labkey.specimen.requirements.SpecimenRequestRequirementProvider; +import org.labkey.specimen.security.permissions.ManageSpecimenActorsPermission; import org.springframework.validation.BindException; import org.springframework.validation.Errors; import org.springframework.web.servlet.ModelAndView; diff --git a/specimen/src/org/labkey/specimen/actions/SpecimenApiController.java b/specimen/src/org/labkey/specimen/actions/SpecimenApiController.java index 5eac6eda..6d8f0e6e 100644 --- a/specimen/src/org/labkey/specimen/actions/SpecimenApiController.java +++ b/specimen/src/org/labkey/specimen/actions/SpecimenApiController.java @@ -30,21 +30,15 @@ import org.labkey.api.security.User; import org.labkey.api.security.UserManager; import org.labkey.api.security.permissions.ReadPermission; -import org.labkey.api.specimen.RequestedSpecimens; import org.labkey.api.specimen.SpecimenManagerNew; import org.labkey.api.specimen.SpecimenRequestException; -import org.labkey.api.specimen.SpecimenRequestManager; import org.labkey.api.specimen.SpecimenRequestStatus; import org.labkey.api.specimen.Vial; import org.labkey.api.specimen.importer.RequestabilityManager; import org.labkey.api.specimen.location.LocationImpl; import org.labkey.api.specimen.location.LocationManager; -import org.labkey.api.specimen.model.AdditiveType; -import org.labkey.api.specimen.model.DerivativeType; import org.labkey.api.specimen.model.PrimaryType; import org.labkey.api.specimen.model.SpecimenTypeSummary; -import org.labkey.api.specimen.requirements.SpecimenRequest; -import org.labkey.api.specimen.security.permissions.ManageRequestsPermission; import org.labkey.api.specimen.security.permissions.RequestSpecimensPermission; import org.labkey.api.specimen.settings.RepositorySettings; import org.labkey.api.specimen.settings.SettingsManager; @@ -52,7 +46,13 @@ import org.labkey.api.util.PageFlowUtil; import org.labkey.api.view.NotFoundException; import org.labkey.api.view.ViewContext; +import org.labkey.specimen.RequestedSpecimens; import org.labkey.specimen.SpecimenManager; +import org.labkey.specimen.SpecimenRequestManager; +import org.labkey.specimen.model.AdditiveType; +import org.labkey.specimen.model.DerivativeType; +import org.labkey.specimen.requirements.SpecimenRequest; +import org.labkey.specimen.security.permissions.ManageRequestsPermission; import org.springframework.validation.BindException; import java.util.ArrayList; diff --git a/specimen/src/org/labkey/specimen/actions/SpecimenController.java b/specimen/src/org/labkey/specimen/actions/SpecimenController.java index 83c09240..cdcc75e6 100644 --- a/specimen/src/org/labkey/specimen/actions/SpecimenController.java +++ b/specimen/src/org/labkey/specimen/actions/SpecimenController.java @@ -1,8 +1,8 @@ package org.labkey.specimen.actions; import org.apache.commons.lang3.StringUtils; -import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.json.JSONObject; @@ -53,13 +53,9 @@ import org.labkey.api.security.permissions.AdminPermission; import org.labkey.api.security.permissions.ReadPermission; import org.labkey.api.security.permissions.UpdatePermission; -import org.labkey.api.specimen.AmbiguousLocationException; -import org.labkey.api.specimen.RequestEventType; -import org.labkey.api.specimen.RequestedSpecimens; import org.labkey.api.specimen.SpecimenManagerNew; import org.labkey.api.specimen.SpecimenQuerySchema; import org.labkey.api.specimen.SpecimenRequestException; -import org.labkey.api.specimen.SpecimenRequestManager; import org.labkey.api.specimen.SpecimenRequestStatus; import org.labkey.api.specimen.SpecimenSchema; import org.labkey.api.specimen.Vial; @@ -67,28 +63,16 @@ import org.labkey.api.specimen.importer.SimpleSpecimenImporter; import org.labkey.api.specimen.location.LocationImpl; import org.labkey.api.specimen.location.LocationManager; -import org.labkey.api.specimen.model.ExtendedSpecimenRequestView; import org.labkey.api.specimen.model.SpecimenComment; -import org.labkey.api.specimen.model.SpecimenRequestActor; -import org.labkey.api.specimen.model.SpecimenRequestEvent; -import org.labkey.api.specimen.notifications.DefaultRequestNotification; -import org.labkey.api.specimen.notifications.NotificationRecipientSet; import org.labkey.api.specimen.query.SpecimenQueryView; -import org.labkey.api.specimen.requirements.SpecimenRequest; -import org.labkey.api.specimen.requirements.SpecimenRequestRequirement; -import org.labkey.api.specimen.requirements.SpecimenRequestRequirementProvider; -import org.labkey.api.specimen.requirements.SpecimenRequestRequirementType; import org.labkey.api.specimen.security.permissions.EditSpecimenDataPermission; import org.labkey.api.specimen.security.permissions.ManageRequestSettingsPermission; -import org.labkey.api.specimen.security.permissions.ManageRequestsPermission; import org.labkey.api.specimen.security.permissions.RequestSpecimensPermission; import org.labkey.api.specimen.settings.DisplaySettings; import org.labkey.api.specimen.settings.RepositorySettings; import org.labkey.api.specimen.settings.RequestNotificationSettings; import org.labkey.api.specimen.settings.SettingsManager; import org.labkey.api.specimen.settings.StatusSettings; -import org.labkey.api.specimen.view.NotificationBean; -import org.labkey.api.specimen.view.SpecimenRequestNotificationEmailTemplate; import org.labkey.api.study.CohortFilter; import org.labkey.api.study.Dataset; import org.labkey.api.study.MapArrayExcelWriter; @@ -134,19 +118,35 @@ import org.labkey.api.view.ViewBackgroundInfo; import org.labkey.api.view.ViewContext; import org.labkey.api.view.WebPartView; +import org.labkey.specimen.AmbiguousLocationException; +import org.labkey.specimen.RequestEventType; +import org.labkey.specimen.RequestedSpecimens; import org.labkey.specimen.SpecimenManager; +import org.labkey.specimen.SpecimenRequestManager; +import org.labkey.specimen.model.ExtendedSpecimenRequestView; +import org.labkey.specimen.model.SpecimenRequestActor; +import org.labkey.specimen.model.SpecimenRequestEvent; import org.labkey.specimen.notifications.ActorNotificationRecipientSet; +import org.labkey.specimen.notifications.DefaultRequestNotification; +import org.labkey.specimen.notifications.NotificationRecipientSet; import org.labkey.specimen.pipeline.SpecimenArchive; import org.labkey.specimen.pipeline.SpecimenBatch; import org.labkey.specimen.query.SpecimenEventQueryView; import org.labkey.specimen.query.SpecimenRequestQueryView; +import org.labkey.specimen.requirements.SpecimenRequest; +import org.labkey.specimen.requirements.SpecimenRequestRequirement; +import org.labkey.specimen.requirements.SpecimenRequestRequirementProvider; +import org.labkey.specimen.requirements.SpecimenRequestRequirementType; import org.labkey.specimen.security.permissions.ManageDisplaySettingsPermission; import org.labkey.specimen.security.permissions.ManageNewRequestFormPermission; import org.labkey.specimen.security.permissions.ManageNotificationsPermission; import org.labkey.specimen.security.permissions.ManageRequestRequirementsPermission; import org.labkey.specimen.security.permissions.ManageRequestStatusesPermission; +import org.labkey.specimen.security.permissions.ManageRequestsPermission; import org.labkey.specimen.security.permissions.ManageSpecimenActorsPermission; import org.labkey.specimen.security.permissions.SetSpecimenCommentsPermission; +import org.labkey.specimen.view.NotificationBean; +import org.labkey.specimen.view.SpecimenRequestNotificationEmailTemplate; import org.labkey.specimen.view.SpecimenSearchWebPart; import org.labkey.specimen.view.SpecimenWebPart; import org.springframework.validation.BindException; diff --git a/specimen/src/org/labkey/specimen/actions/ViewRequestsHeaderBean.java b/specimen/src/org/labkey/specimen/actions/ViewRequestsHeaderBean.java index 815eb4fb..b7888b51 100644 --- a/specimen/src/org/labkey/specimen/actions/ViewRequestsHeaderBean.java +++ b/specimen/src/org/labkey/specimen/actions/ViewRequestsHeaderBean.java @@ -1,9 +1,9 @@ package org.labkey.specimen.actions; -import org.labkey.api.specimen.SpecimenRequestManager; import org.labkey.api.specimen.SpecimenRequestStatus; -import org.labkey.specimen.query.SpecimenRequestQueryView; import org.labkey.api.view.ViewContext; +import org.labkey.specimen.SpecimenRequestManager; +import org.labkey.specimen.query.SpecimenRequestQueryView; import java.util.List; diff --git a/specimen/src/org/labkey/specimen/importer/AbstractSpecimenTask.java b/specimen/src/org/labkey/specimen/importer/AbstractSpecimenTask.java index ea9130d6..104ca92c 100644 --- a/specimen/src/org/labkey/specimen/importer/AbstractSpecimenTask.java +++ b/specimen/src/org/labkey/specimen/importer/AbstractSpecimenTask.java @@ -27,7 +27,6 @@ import org.labkey.api.pipeline.TaskFactory; import org.labkey.api.specimen.importer.SpecimenImporter; import org.labkey.api.specimen.pipeline.SpecimenJobSupport; -import org.labkey.api.specimen.writer.SpecimenArchiveDataTypes; import org.labkey.api.study.SpecimenService; import org.labkey.api.study.SpecimenTransform; import org.labkey.api.study.Study; @@ -40,6 +39,7 @@ import org.labkey.api.writer.FileSystemFile; import org.labkey.api.writer.VirtualFile; import org.labkey.api.writer.ZipUtil; +import org.labkey.specimen.writer.SpecimenArchiveDataTypes; import java.io.File; import java.io.IOException; diff --git a/specimen/src/org/labkey/specimen/importer/SpecimenSchemaImporter.java b/specimen/src/org/labkey/specimen/importer/SpecimenSchemaImporter.java index db8732e2..3b497672 100644 --- a/specimen/src/org/labkey/specimen/importer/SpecimenSchemaImporter.java +++ b/specimen/src/org/labkey/specimen/importer/SpecimenSchemaImporter.java @@ -31,7 +31,6 @@ import org.labkey.api.specimen.SpecimenSchema; import org.labkey.api.specimen.importer.ImportTemplate; import org.labkey.api.specimen.model.SpecimenTablesProvider; -import org.labkey.api.specimen.writer.SpecimenArchiveDataTypes; import org.labkey.api.study.SpecimenTablesTemplate; import org.labkey.api.study.importer.SimpleStudyImportContext; import org.labkey.api.study.importer.SimpleStudyImporter; @@ -42,6 +41,7 @@ import org.labkey.data.xml.TableType; import org.labkey.data.xml.TablesDocument; import org.labkey.data.xml.TablesType; +import org.labkey.specimen.writer.SpecimenArchiveDataTypes; import org.labkey.study.xml.StudyDocument; import org.springframework.validation.BindException; diff --git a/specimen/src/org/labkey/specimen/importer/SpecimenSettingsImporter.java b/specimen/src/org/labkey/specimen/importer/SpecimenSettingsImporter.java index 512c32bd..c2389d48 100644 --- a/specimen/src/org/labkey/specimen/importer/SpecimenSettingsImporter.java +++ b/specimen/src/org/labkey/specimen/importer/SpecimenSettingsImporter.java @@ -20,29 +20,29 @@ import org.labkey.api.data.Container; import org.labkey.api.security.GroupManager; import org.labkey.api.security.SecurityManager; -import org.labkey.api.specimen.SpecimenRequestManager; -import org.labkey.api.specimen.SpecimenRequestManager.SpecimenRequestInput; import org.labkey.api.specimen.SpecimenRequestStatus; -import org.labkey.specimen.actions.ManageReqsBean; import org.labkey.api.specimen.importer.RequestabilityManager; import org.labkey.api.specimen.location.LocationCache; import org.labkey.api.specimen.location.LocationImpl; -import org.labkey.api.specimen.model.SpecimenRequestActor; -import org.labkey.api.specimen.requirements.RequirementType; -import org.labkey.api.specimen.requirements.SpecimenRequestRequirement; -import org.labkey.api.specimen.requirements.SpecimenRequestRequirementProvider; -import org.labkey.api.specimen.requirements.SpecimenRequestRequirementType; import org.labkey.api.specimen.settings.DisplaySettings; import org.labkey.api.specimen.settings.RepositorySettings; import org.labkey.api.specimen.settings.RequestNotificationSettings; import org.labkey.api.specimen.settings.SettingsManager; import org.labkey.api.specimen.settings.StatusSettings; -import org.labkey.api.specimen.writer.SpecimenArchiveDataTypes; import org.labkey.api.study.StudyService; import org.labkey.api.study.importer.SimpleStudyImportContext; import org.labkey.api.study.importer.SimpleStudyImporter; import org.labkey.api.writer.VirtualFile; import org.labkey.security.xml.GroupType; +import org.labkey.specimen.SpecimenRequestManager; +import org.labkey.specimen.SpecimenRequestManager.SpecimenRequestInput; +import org.labkey.specimen.actions.ManageReqsBean; +import org.labkey.specimen.model.SpecimenRequestActor; +import org.labkey.specimen.requirements.RequirementType; +import org.labkey.specimen.requirements.SpecimenRequestRequirement; +import org.labkey.specimen.requirements.SpecimenRequestRequirementProvider; +import org.labkey.specimen.requirements.SpecimenRequestRequirementType; +import org.labkey.specimen.writer.SpecimenArchiveDataTypes; import org.labkey.study.xml.DefaultRequirementType; import org.labkey.study.xml.DefaultRequirementsType; import org.labkey.study.xml.SpecimenRepositoryType; diff --git a/specimen/src/org/labkey/specimen/model/AdditiveType.java b/specimen/src/org/labkey/specimen/model/AdditiveType.java new file mode 100644 index 00000000..e8b244e5 --- /dev/null +++ b/specimen/src/org/labkey/specimen/model/AdditiveType.java @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2009-2015 LabKey Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * 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.specimen.model; + +import org.labkey.api.data.Container; +import org.labkey.api.specimen.model.AdditiveTypeDomainKind; +import org.labkey.api.study.AbstractStudyCachable; + +import java.util.Map; + +public class AdditiveType extends AbstractStudyCachable +{ + private long _rowId; // serial NOT NULL, + private Container _container; // entityid NOT NULL, + private String _ldmsAdditiveCode; // character varying(5), + private String _labwareAdditiveCode; // character varying(5), + private String _additive; // character varying(100), + private int _externalId; // integer NOT NULL default 0 + + public AdditiveType(Container container, Map map) + { + super(); + _rowId = (int) map.get(AdditiveTypeDomainKind.ROWID); + _container = container; + _ldmsAdditiveCode = (String) map.get(AdditiveTypeDomainKind.LDMSADDITIVECODE); + _labwareAdditiveCode = (String) map.get(AdditiveTypeDomainKind.LABWAREADDITIVECODE); + _additive = (String) map.get(AdditiveTypeDomainKind.ADDITIVE); + _externalId = (int) map.get(AdditiveTypeDomainKind.EXTERNALID); + } + + @Override + public Object getPrimaryKey() + { + return _rowId; + } + + public long getRowId() + { + return _rowId; + } + + public void setRowId(long rowId) + { + _rowId = rowId; + } + + public int getExternalId() + { + return _externalId; + } + + public void setExternalId(int externalId) + { + _externalId = externalId; + } + + @Override + public Container getContainer() + { + return _container; + } + + @Override + public void setContainer(Container container) + { + _container = container; + } + + public String getLdmsAdditiveCode() + { + return _ldmsAdditiveCode; + } + + public void setLdmsAdditiveCode(String ldmsAdditiveCode) + { + _ldmsAdditiveCode = ldmsAdditiveCode; + } + + public String getLabwareAdditiveCode() + { + return _labwareAdditiveCode; + } + + public void setLabwareAdditiveCode(String labwareAdditiveCode) + { + _labwareAdditiveCode = labwareAdditiveCode; + } + + public String getAdditive() + { + return _additive; + } + + public void setAdditive(String additive) + { + _additive = additive; + } +} \ No newline at end of file diff --git a/specimen/src/org/labkey/specimen/model/DerivativeType.java b/specimen/src/org/labkey/specimen/model/DerivativeType.java new file mode 100644 index 00000000..28f9d2c0 --- /dev/null +++ b/specimen/src/org/labkey/specimen/model/DerivativeType.java @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2009-2015 LabKey Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * 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.specimen.model; + +import org.labkey.api.data.Container; +import org.labkey.api.specimen.model.DerivativeTypeDomainKind; +import org.labkey.api.study.AbstractStudyCachable; + +import java.util.Map; + +/* + * User: brittp + * Date: Dec 18, 2008 + * Time: 12:48:03 PM + */ + +public class DerivativeType extends AbstractStudyCachable +{ + private long _rowId; // serial NOT NULL, + private Container _container; // entityid NOT NULL, + private String _ldmsDerivativeCode; // character varying(5), + private String _labwareDerivativeCode; // character varying(5), + private String _derivative; // character varying(100), + private int _externalId; // integer NOT NULL default 0 + + public DerivativeType(Container container, Map map) + { + super(); + _rowId = (int) map.get(DerivativeTypeDomainKind.ROWID); + _container = container; + _ldmsDerivativeCode = (String) map.get(DerivativeTypeDomainKind.LDMSDERIVATIVECODE); + _labwareDerivativeCode = (String) map.get(DerivativeTypeDomainKind.LABWAREDERIVATIVECODE); + _derivative = (String) map.get(DerivativeTypeDomainKind.DERIVATIVE); + _externalId = (int) map.get(DerivativeTypeDomainKind.EXTERNALID); + } + + @Override + public Object getPrimaryKey() + { + return _rowId; + } + + public long getRowId() + { + return _rowId; + } + + public void setRowId(long rowId) + { + _rowId = rowId; + } + + public int getExternalId() + { + return _externalId; + } + + public void setExternalId(int externalId) + { + _externalId = externalId; + } + + @Override + public Container getContainer() + { + return _container; + } + + @Override + public void setContainer(Container container) + { + _container = container; + } + + public String getLdmsDerivativeCode() + { + return _ldmsDerivativeCode; + } + + public void setLdmsDerivativeCode(String ldmsDerivativeCode) + { + _ldmsDerivativeCode = ldmsDerivativeCode; + } + + public String getLabwareDerivativeCode() + { + return _labwareDerivativeCode; + } + + public void setLabwareDerivativeCode(String labwareDerivativeCode) + { + _labwareDerivativeCode = labwareDerivativeCode; + } + + public String getDerivative() + { + return _derivative; + } + + public void setDerivative(String derivative) + { + _derivative = derivative; + } +} \ No newline at end of file diff --git a/specimen/src/org/labkey/specimen/model/ExtendedSpecimenRequestView.java b/specimen/src/org/labkey/specimen/model/ExtendedSpecimenRequestView.java new file mode 100644 index 00000000..7eab75c5 --- /dev/null +++ b/specimen/src/org/labkey/specimen/model/ExtendedSpecimenRequestView.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2013 LabKey Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.labkey.specimen.model; + +import org.labkey.api.data.Entity; + +/** + * User: Nick Arnold + * Date: 3/13/13 + */ +public class ExtendedSpecimenRequestView extends Entity +{ + private boolean _active; + private String _body; + + public static ExtendedSpecimenRequestView createView(String body) + { + ExtendedSpecimenRequestView view = new ExtendedSpecimenRequestView(); + view.setBody(body); + view.setActive(true); + return view; + } + + public String getBody() + { + return _body; + } + + public void setBody(String body) + { + _body = body; + } + + public boolean isActive() + { + return _active; + } + + public void setActive(boolean active) + { + _active = active; + } +} diff --git a/specimen/src/org/labkey/specimen/model/SpecimenRequestActor.java b/specimen/src/org/labkey/specimen/model/SpecimenRequestActor.java new file mode 100644 index 00000000..e8377212 --- /dev/null +++ b/specimen/src/org/labkey/specimen/model/SpecimenRequestActor.java @@ -0,0 +1,156 @@ +/* + * 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.specimen.model; + +import org.labkey.api.data.Container; +import org.labkey.api.data.TableInfo; +import org.labkey.api.specimen.SpecimenSchema; +import org.labkey.api.study.StudyCachable; +import org.labkey.api.util.MemTracker; +import org.labkey.specimen.requirements.DefaultActor; + +/** + * User: brittp + * Date: Feb 8, 2006 + * Time: 4:18:20 PM + */ +public class SpecimenRequestActor extends DefaultActor implements StudyCachable, Cloneable +{ + private int _rowId; + private Integer _sortOrder; + private Container _container; + private String _label; + private boolean _perSite; + private boolean _mutable = true; + + public SpecimenRequestActor() + { + MemTracker.getInstance().put(this); + } + + protected void verifyMutability() + { + if (!_mutable) + throw new IllegalStateException("Cached objects are immutable; createMutable must be called first."); + } + + public boolean isMutable() + { + return _mutable; + } + + protected void unlock() + { + _mutable = true; + } + + @Override + public void lock() + { + _mutable = false; + } + + @Override + public SpecimenRequestActor createMutable() + { + try + { + SpecimenRequestActor obj = (SpecimenRequestActor) clone(); + obj.unlock(); + return obj; + } + catch (CloneNotSupportedException e) + { + throw new RuntimeException(e); + } + } + + @Override + public Container getContainer() + { + return _container; + } + + public void setContainer(Container container) + { + verifyMutability(); + _container = container; + } + + public String getLabel() + { + return _label; + } + + public void setLabel(String label) + { + verifyMutability(); + _label = label; + } + + @Override + public Object getPrimaryKey() + { + return getRowId(); + } + + public int getRowId() + { + return _rowId; + } + + public void setRowId(int rowId) + { + verifyMutability(); + _rowId = rowId; + } + + public Integer getSortOrder() + { + return _sortOrder; + } + + public void setSortOrder(Integer sortOrder) + { + verifyMutability(); + _sortOrder = sortOrder; + } + + public boolean isPerSite() + { + return _perSite; + } + + public void setPerSite(boolean perSite) + { + verifyMutability(); + _perSite = perSite; + } + + @Override + public String getGroupName() + { + return getLabel(); + } + + + @Override + protected TableInfo getTableInfo() + { + return SpecimenSchema.get().getTableInfoSampleRequestActor(); + } +} diff --git a/specimen/src/org/labkey/specimen/model/SpecimenRequestEvent.java b/specimen/src/org/labkey/specimen/model/SpecimenRequestEvent.java new file mode 100644 index 00000000..2549c382 --- /dev/null +++ b/specimen/src/org/labkey/specimen/model/SpecimenRequestEvent.java @@ -0,0 +1,163 @@ +/* + * 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.specimen.model; + +import org.jetbrains.annotations.NotNull; +import org.labkey.api.attachments.AttachmentParent; +import org.labkey.api.attachments.AttachmentType; +import org.labkey.api.data.Container; +import org.labkey.api.study.AbstractStudyCachable; + +import java.util.Date; + +/** + * User: brittp + * Date: Feb 24, 2006 + * Time: 1:56:09 PM + */ +public class SpecimenRequestEvent extends AbstractStudyCachable implements AttachmentParent +{ + private int _rowId; + private String _entityId; + private int _createdBy; + private long _created; + private Container _container; + private int _requestId; + private Integer _requirementId; + private String _comments; + private String _entryType; + + public String getComments() + { + return _comments; + } + + public void setComments(String comments) + { + verifyMutability(); + _comments = comments; + } + + @Override + public Container getContainer() + { + return _container; + } + + @Override + public String getContainerId() + { + return _container.getId(); + } + + @Override + public void setContainer(Container container) + { + verifyMutability(); + _container = container; + } + + public Date getCreated() + { + return new Date(_created); + } + + public void setCreated(Date created) + { + verifyMutability(); + _created = created.getTime(); + } + + public int getCreatedBy() + { + return _createdBy; + } + + public void setCreatedBy(int createdBy) + { + verifyMutability(); + _createdBy = createdBy; + } + + @Override + public String getEntityId() + { + return _entityId; + } + + public void setEntityId(String entityId) + { + verifyMutability(); + _entityId = entityId; + } + + public String getEntryType() + { + return _entryType; + } + + public void setEntryType(String entryType) + { + verifyMutability(); + _entryType = entryType; + } + + public int getRequestId() + { + return _requestId; + } + + public void setRequestId(int requestId) + { + verifyMutability(); + _requestId = requestId; + } + + @Override + public Object getPrimaryKey() + { + return getRowId(); + } + + public int getRowId() + { + return _rowId; + } + + public void setRowId(int rowId) + { + verifyMutability(); + _rowId = rowId; + } + + public Integer getRequirementId() + { + return _requirementId; + } + + public void setRequirementId(Integer requirementId) + { + verifyMutability(); + _requirementId = requirementId; + } + + @Override + public @NotNull AttachmentType getAttachmentType() + { + return SpecimenRequestEventType.get(); + } +} diff --git a/specimen/src/org/labkey/specimen/model/SpecimenRequestEventType.java b/specimen/src/org/labkey/specimen/model/SpecimenRequestEventType.java new file mode 100644 index 00000000..a7bcd30c --- /dev/null +++ b/specimen/src/org/labkey/specimen/model/SpecimenRequestEventType.java @@ -0,0 +1,47 @@ +/* + * 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.specimen.model; + +import org.jetbrains.annotations.NotNull; +import org.labkey.api.attachments.AttachmentType; +import org.labkey.api.data.SQLFragment; +import org.labkey.api.specimen.SpecimenSchema; + +public class SpecimenRequestEventType implements AttachmentType +{ + private static final SpecimenRequestEventType INSTANCE = new SpecimenRequestEventType(); + + public static SpecimenRequestEventType get() + { + return INSTANCE; + } + + private SpecimenRequestEventType() + { + } + + @Override + public @NotNull String getUniqueName() + { + return getClass().getName(); + } + + @Override + public void addWhereSql(SQLFragment sql, String parentColumn, String documentNameColumn) + { + sql.append(parentColumn).append(" IN (SELECT EntityId FROM ").append(SpecimenSchema.get().getTableInfoSampleRequestEvent(), "sre").append(")"); + } +} \ No newline at end of file diff --git a/specimen/src/org/labkey/specimen/notifications/ActorNotificationRecipientSet.java b/specimen/src/org/labkey/specimen/notifications/ActorNotificationRecipientSet.java index 07289a6e..08dab9be 100644 --- a/specimen/src/org/labkey/specimen/notifications/ActorNotificationRecipientSet.java +++ b/specimen/src/org/labkey/specimen/notifications/ActorNotificationRecipientSet.java @@ -20,15 +20,14 @@ import org.labkey.api.security.User; import org.labkey.api.specimen.location.LocationImpl; import org.labkey.api.specimen.location.LocationManager; -import org.labkey.api.specimen.model.SpecimenRequestActor; -import org.labkey.api.specimen.notifications.NotificationRecipientSet; -import org.labkey.api.specimen.requirements.SpecimenRequestRequirementProvider; import org.labkey.api.util.HtmlString; import org.labkey.api.util.HtmlStringBuilder; import org.labkey.api.util.PageFlowUtil; import org.labkey.api.util.SafeToRender; import org.labkey.api.view.ActionURL; import org.labkey.specimen.actions.ShowGroupMembersAction; +import org.labkey.specimen.model.SpecimenRequestActor; +import org.labkey.specimen.requirements.SpecimenRequestRequirementProvider; /** * User: brittp diff --git a/specimen/src/org/labkey/specimen/notifications/DefaultRequestNotification.java b/specimen/src/org/labkey/specimen/notifications/DefaultRequestNotification.java new file mode 100644 index 00000000..6d7ef033 --- /dev/null +++ b/specimen/src/org/labkey/specimen/notifications/DefaultRequestNotification.java @@ -0,0 +1,147 @@ +/* + * 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.specimen.notifications; + +import org.jetbrains.annotations.NotNull; +import org.labkey.api.attachments.Attachment; +import org.labkey.api.attachments.AttachmentFile; +import org.labkey.api.attachments.AttachmentService; +import org.labkey.api.attachments.ByteArrayAttachmentFile; +import org.labkey.api.data.ColumnHeaderType; +import org.labkey.api.data.ExcelWriter; +import org.labkey.api.data.TSVWriter; +import org.labkey.api.specimen.Vial; +import org.labkey.api.specimen.query.SpecimenQueryView; +import org.labkey.api.specimen.settings.RequestNotificationSettings; +import org.labkey.api.specimen.settings.SettingsManager; +import org.labkey.api.view.ViewContext; +import org.labkey.specimen.model.SpecimenRequestEvent; +import org.labkey.specimen.requirements.SpecimenRequest; +import org.labkey.specimen.requirements.SpecimenRequestRequirement; + +import java.io.IOException; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +/** + * User: brittp + * Date: May 4, 2007 + * Time: 3:41:44 PM + */ +public class DefaultRequestNotification +{ + protected List _recipients; + protected SpecimenRequest _request; + protected String _eventSummary; + protected SpecimenRequestRequirement _specimenRequestRequirement; + protected String _comments; + protected SpecimenRequestEvent _event; + + public DefaultRequestNotification(SpecimenRequest request, List recipients, String eventSummary, + SpecimenRequestEvent event, String comments, SpecimenRequestRequirement specimenRequestRequirement, + ViewContext context) throws Exception + { + _request = request; + _recipients = recipients; + _eventSummary = eventSummary; + _comments = comments; + _specimenRequestRequirement = specimenRequestRequirement; + _event = event; + addSpecimenListFileIfNeeded(context); + } + + public final String getSpecimenListHTML(ViewContext context) throws SQLException, IOException + { + List vials = getSpecimenList(); + if (vials != null && vials.size() > 0) + { + SpecimenQueryView view = SpecimenQueryView.createView(context, vials, SpecimenQueryView.ViewType.VIALS_EMAIL); + view.setDisableLowVialIndicators(true); + view.getSettings().setMaxRows(-1); + return view.getSimpleHtmlTable(); + } + return null; + } + + private void addSpecimenListFileIfNeeded(ViewContext context) throws Exception + { + RequestNotificationSettings settings = SettingsManager.get().getRequestNotificationSettings(_request.getContainer()); + if (RequestNotificationSettings.SpecimensAttachmentEnum.ExcelAttachment == settings.getSpecimensAttachmentEnum() || + RequestNotificationSettings.SpecimensAttachmentEnum.TextAttachment == settings.getSpecimensAttachmentEnum()) + { + final ByteArrayAttachmentFile specimenListFile; + List vials = getSpecimenList(); + if (vials != null && vials.size() > 0) + { + SpecimenQueryView view = SpecimenQueryView.createView(context, vials, SpecimenQueryView.ViewType.VIALS_EMAIL); + view.getSettings().setMaxRows(-1); + view.setDisableLowVialIndicators(true); + if (RequestNotificationSettings.SpecimensAttachmentEnum.ExcelAttachment == settings.getSpecimensAttachmentEnum()) + specimenListFile = view.exportToExcelFile(ExcelWriter.ExcelDocumentType.xls, null, null, false); + else + specimenListFile = view.exportToTsvFile(TSVWriter.DELIM.TAB, TSVWriter.QUOTE.DOUBLE, ColumnHeaderType.Caption, null, null, false); + if (null != specimenListFile) + { + List attachments = new ArrayList<>(); + attachments.add(specimenListFile); + AttachmentService.get().addAttachments(_event, attachments, context.getUser()); + } + } + } + } + + protected List getSpecimenList() + { + return _request.getVials(); + } + + public SpecimenRequestEvent getEvent() + { + return _event; + } + + public @NotNull List getAttachments() + { + return AttachmentService.get().getAttachments(_event); + } + + public String getComments() + { + return _comments; + } + + public SpecimenRequestRequirement getRequirement() + { + return _specimenRequestRequirement; + } + + final public List getRecipients() + { + return _recipients; + } + + final public SpecimenRequest getSpecimenRequest() + { + return _request; + } + + final public String getEventSummary() + { + return _eventSummary; + } +} diff --git a/specimen/src/org/labkey/specimen/notifications/NotificationRecipientSet.java b/specimen/src/org/labkey/specimen/notifications/NotificationRecipientSet.java new file mode 100644 index 00000000..cf28a837 --- /dev/null +++ b/specimen/src/org/labkey/specimen/notifications/NotificationRecipientSet.java @@ -0,0 +1,120 @@ +/* + * 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.specimen.notifications; + +import javax.mail.Address; + +/** + * User: brittp + * Date: May 4, 2007 + * Time: 3:35:26 PM + */ +public class NotificationRecipientSet +{ + private String[] _addresses; + private boolean[] _addressInactiveBits; + private int _countInactiveEmailAddresses = 0; + + public NotificationRecipientSet(Address[] addresses) + { + _addresses = new String[addresses.length]; + _addressInactiveBits = new boolean[addresses.length]; // all false by default + int i = 0; + for (Address address : addresses) + _addresses[i++] = address.toString(); + } + + protected NotificationRecipientSet() + { + } + + public String[] getAllEmailAddresses() + { + return _addresses; + } + + public String[] getEmailAddresses(boolean includeInactiveUsers) + { + if (0 == _countInactiveEmailAddresses || includeInactiveUsers) + return _addresses; + String[] addresses = new String[_addresses.length - _countInactiveEmailAddresses]; + int k = 0; + for (int i = 0; i < _addresses.length; i += 1) + { + if (!_addressInactiveBits[i]) + { + addresses[k] = _addresses[i]; + k += 0; + } + } + return addresses; + } + + protected void setEmailAddresses(String[] addresses, boolean[] inactiveBits) + { + _addresses = addresses; + assert (addresses.length == inactiveBits.length); + _addressInactiveBits = inactiveBits; + + for (boolean inactive : _addressInactiveBits) + if (inactive) + _countInactiveEmailAddresses += 1; + } + + public String getShortRecipientDescription() + { + StringBuilder builder = new StringBuilder(); + boolean first = true; + for (String email : _addresses) + { + if (!first) + builder.append(", "); + builder.append(email); + first = false; + } + return builder.toString(); + } + + public String getLongRecipientDescription() + { + return getShortRecipientDescription(); + } + + public String getEmailAddressesAsString(String separator) + { + StringBuilder emailList = new StringBuilder(); + assert (null != _addresses && null != _addressInactiveBits && _addresses.length == _addressInactiveBits.length); + boolean first = true; + for (int i = 0; i < _addresses.length; i += 1) + { + if (!first) + emailList.append(separator); + if (_addressInactiveBits[i]) + emailList.append(""); + emailList.append(_addresses[i]); + if (_addressInactiveBits[i]) + emailList.append(""); + first = false; + } + return emailList.toString(); + } + + public boolean hasInactiveEmailAddress() + { + return _countInactiveEmailAddresses > 0; + } +} diff --git a/specimen/src/org/labkey/specimen/query/SpecimenRequestQueryView.java b/specimen/src/org/labkey/specimen/query/SpecimenRequestQueryView.java index 506424f7..d9390ef1 100644 --- a/specimen/src/org/labkey/specimen/query/SpecimenRequestQueryView.java +++ b/specimen/src/org/labkey/specimen/query/SpecimenRequestQueryView.java @@ -29,10 +29,8 @@ import org.labkey.api.query.UserSchema; import org.labkey.api.security.User; import org.labkey.api.specimen.SpecimenQuerySchema; -import org.labkey.api.specimen.SpecimenRequestManager; import org.labkey.api.specimen.SpecimenRequestStatus; import org.labkey.api.specimen.query.BaseSpecimenQueryView; -import org.labkey.api.specimen.security.permissions.ManageRequestsPermission; import org.labkey.api.specimen.security.permissions.RequestSpecimensPermission; import org.labkey.api.specimen.settings.SettingsManager; import org.labkey.api.study.Study; @@ -42,9 +40,11 @@ import org.labkey.api.view.ActionURL; import org.labkey.api.view.NavTree; import org.labkey.api.view.ViewContext; +import org.labkey.specimen.SpecimenRequestManager; import org.labkey.specimen.actions.SpecimenController.DeleteRequestAction; import org.labkey.specimen.actions.SpecimenController.ManageRequestAction; import org.labkey.specimen.actions.SpecimenController.SubmitRequestAction; +import org.labkey.specimen.security.permissions.ManageRequestsPermission; import java.io.IOException; import java.io.Writer; diff --git a/specimen/src/org/labkey/specimen/requirements/DefaultActor.java b/specimen/src/org/labkey/specimen/requirements/DefaultActor.java new file mode 100644 index 00000000..2b9ab970 --- /dev/null +++ b/specimen/src/org/labkey/specimen/requirements/DefaultActor.java @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2008-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.specimen.requirements; + +import org.jetbrains.annotations.Nullable; +import org.labkey.api.data.DbScope; +import org.labkey.api.data.Table; +import org.labkey.api.data.TableInfo; +import org.labkey.api.security.Group; +import org.labkey.api.security.InvalidGroupMembershipException; +import org.labkey.api.security.PrincipalType; +import org.labkey.api.security.SecurityManager; +import org.labkey.api.security.User; +import org.labkey.api.security.UserManager; +import org.labkey.api.specimen.SpecimenSchema; +import org.labkey.api.specimen.location.LocationManager; +import org.labkey.api.study.Location; +import org.labkey.api.util.ExceptionUtil; +import org.labkey.api.util.Pair; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +/** + * User: brittp + * Date: Jun 7, 2007 + * Time: 4:29:29 PM + */ +public abstract class DefaultActor> implements RequirementActor +{ + @Override + public void addMembers(@Nullable Location location, User... users) + { + Integer groupId = getGroupId(location, true); + Group group = SecurityManager.getGroup(groupId); + + for (User user : users) + { + try + { + SecurityManager.addMember(group, user); + } + catch (InvalidGroupMembershipException e) + { + // Best effort, but log any exceptions. Exception is unlikely at this point, since + // actors don't currently support groups as members. + ExceptionUtil.logExceptionToMothership(null, e); + } + } + } + + @Override + public User[] getMembers() + { + return getMembers(null); + } + + @Override + public User[] getMembers(@Nullable Location location) + { + Integer groupId = getGroupId(location, false); + if (groupId == null) + return new User[0]; + List> userIds = SecurityManager.getGroupMemberNamesAndIds(groupId, true); // include active and inactive + User[] users = new User[userIds.size()]; + Iterator> idIt = userIds.iterator(); + for (int i = 0; i < users.length; i++) + users[i] = UserManager.getUser(idIt.next().getKey()); + return users; + } + + @Override + public void removeMembers(User... members) + { + removeMembers(null, members); + } + + @Override + public void removeMembers(@Nullable Location location, User... members) + { + Integer groupId = getGroupId(location, false); + if (groupId == null) + throw new IllegalStateException("Group Id does not exist."); + Group group = SecurityManager.getGroup(groupId); + if (group == null) + throw new IllegalStateException("Group does not exist."); + for (User member : members) + SecurityManager.deleteMember(group, member); + } + + public Group getGroup(@Nullable Location location) + { + Integer groupId = getGroupId(location, false); + return SecurityManager.getGroup(groupId); + } + + public Integer getGroupId(boolean createIfMissing) + { + return getGroupId(null, createIfMissing); + } + + public Integer getGroupId(@Nullable Location location, boolean createIfMissing) + { + String groupName = getGroupName() + " " + getPrimaryKey(); + Integer groupId; + if (location != null) + { + groupId = SecurityManager.getGroupId(getContainer(), groupName, location.getEntityId(), false); + if (groupId == null && createIfMissing) + groupId = SecurityManager.createGroup(getContainer(), groupName, PrincipalType.MODULE, location.getEntityId()).getUserId(); + } + else + { + groupId = SecurityManager.getGroupId(getContainer(), groupName, false); + if (groupId == null && createIfMissing) + groupId = SecurityManager.createGroup(getContainer(), groupName, PrincipalType.MODULE).getUserId(); + } + return groupId; + } + + @Override + public void deleteAllGroups() + { + List groupsToDelete = new ArrayList<>(); + List locations = LocationManager.get().getLocations(getContainer()); + for (Location location : locations) + { + Integer id = getGroupId(location, false); + if (id != null) + groupsToDelete.add(id); + } + Integer id = getGroupId(false); + if (id != null) + groupsToDelete.add(id); + + for (Integer groupId : groupsToDelete) + { + Group group = SecurityManager.getGroup(groupId); + if (group != null) + SecurityManager.deleteGroup(group); + } + } + + @Override + public A create(User user) + { + if (getContainer() == null) + throw new IllegalArgumentException("Container must be set for all requirements."); + return Table.insert(user, getTableInfo(), (A) this); + } + + @Override + public A update(User user) + { + return Table.update(user, getTableInfo(), (A) this, getPrimaryKey()); + } + + @Override + public void delete() + { + DbScope scope = SpecimenSchema.get().getScope(); + try (DbScope.Transaction transaction = scope.ensureTransaction()) + { + deleteAllGroups(); + Table.delete(getTableInfo(), getPrimaryKey()); + + transaction.commit(); + } + } + + protected abstract TableInfo getTableInfo(); +} diff --git a/specimen/src/org/labkey/specimen/requirements/DefaultRequirement.java b/specimen/src/org/labkey/specimen/requirements/DefaultRequirement.java new file mode 100644 index 00000000..9117ea9d --- /dev/null +++ b/specimen/src/org/labkey/specimen/requirements/DefaultRequirement.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2008-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.specimen.requirements; + +import org.labkey.api.data.Table; +import org.labkey.api.data.TableInfo; +import org.labkey.api.security.User; + +/** + * User: brittp + * Date: Jun 7, 2007 + * Time: 4:29:24 PM + */ +public abstract class DefaultRequirement> implements Requirement +{ + @Override + public R update(User user) + { + return Table.update(user, getTableInfo(), (R) this, getPrimaryKeyValue()); + } + + @Override + public void delete() + { + Table.delete(getTableInfo(), getPrimaryKeyValue()); + } + + @Override + public R persist(User user, String ownerEntityId) + { + if (getContainer() == null) + throw new IllegalArgumentException("Container must be set for all requirements."); + if (ownerEntityId == null) + throw new IllegalArgumentException("Owner entity Id must be provided for all requirements."); + + R mutable = createMutable(); + mutable.setOwnerEntityId(ownerEntityId); + return Table.insert(user, getTableInfo(), mutable); + } + + protected abstract TableInfo getTableInfo(); + protected abstract Object getPrimaryKeyValue(); +} diff --git a/specimen/src/org/labkey/specimen/requirements/DefaultRequirementProvider.java b/specimen/src/org/labkey/specimen/requirements/DefaultRequirementProvider.java new file mode 100644 index 00000000..1468a56c --- /dev/null +++ b/specimen/src/org/labkey/specimen/requirements/DefaultRequirementProvider.java @@ -0,0 +1,247 @@ +/* + * Copyright (c) 2008-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.specimen.requirements; + +import org.labkey.api.data.Container; +import org.labkey.api.data.DbScope; +import org.labkey.api.data.PropertyManager; +import org.labkey.api.data.SimpleFilter; +import org.labkey.api.data.Sort; +import org.labkey.api.data.TableInfo; +import org.labkey.api.data.TableSelector; +import org.labkey.api.query.FieldKey; +import org.labkey.api.security.User; +import org.labkey.api.specimen.SpecimenSchema; +import org.labkey.api.util.GUID; + +import java.lang.reflect.Array; +import java.util.Arrays; +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; + +/** + * User: brittp + * Date: Jun 4, 2007 + * Time: 2:36:43 PM + */ +public abstract class DefaultRequirementProvider, A extends RequirementActor> + implements RequirementProvider +{ + private final Class _requirementClass; + private final Class _actorClass; + + public DefaultRequirementProvider(Class requirementClass, Class actorClass) + { + _requirementClass = requirementClass; + _actorClass = actorClass; + } + + @Override + public R[] getDefaultRequirements(Container container, RequirementType type) + { + String ownerId = getDefaultRequirementPlaceholder(container, type, false); + if (ownerId == null) + return (R[]) Array.newInstance(_requirementClass, 0); + else + return getRequirements(container, ownerId); + } + + public R[] getDefaultRequirements(Container container) + { + Set defaultRequirements = new HashSet<>(); + for (RequirementType type : getRequirementTypes()) + defaultRequirements.addAll(Arrays.asList(getDefaultRequirements(container, type))); + return defaultRequirements.toArray((R[]) Array.newInstance(_requirementClass, defaultRequirements.size())); + } + + @Override + public R createDefaultRequirement(User user, R requirement, RequirementType type) + { + String owner = getDefaultRequirementPlaceholder(requirement.getContainer(), type, true); + requirement = requirement.createMutable(); + requirement.setOwnerEntityId(owner); + return requirement.persist(user, owner); + } + + private synchronized String getDefaultRequirementPlaceholder(final Container container, RequirementType type, boolean createIfMissing) + { + PropertyManager.PropertyMap props = PropertyManager.getWritableProperties(container, + "DefaultRequirement: " + getClass().getSimpleName(), true); + String ownerEntityId = props.get(type.name()); + if (ownerEntityId == null && createIfMissing) + { + ownerEntityId = GUID.makeGUID(); + props.put(type.name(), ownerEntityId); + props.save(); + } + return ownerEntityId; + } + + @Override + public R getRequirement(Container container, Object requirementPrimaryKey) + { + return new TableSelector(getRequirementTableInfo()).getObject(requirementPrimaryKey, _requirementClass); + } + + public R[] getRequirements(Container container, String ownerEntityId) + { + SimpleFilter filter = SimpleFilter.createContainerFilter(container); + filter.addCondition(getOwnerEntityIdColumnName(), ownerEntityId); + + return new TableSelector(getRequirementTableInfo(), filter, getDefaultRequirementSort()).getArray(_requirementClass); + } + + protected Sort getDefaultRequirementSort() + { + return new Sort("RowId"); + } + + public R[] getRequirements(Container container) + { + SimpleFilter filter = SimpleFilter.createContainerFilter(container); + return new TableSelector(getRequirementTableInfo(), filter, getDefaultRequirementSort()).getArray(_requirementClass); + } + + @Override + public A[] getActors(Container c) + { + SimpleFilter filter = SimpleFilter.createContainerFilter(c); + + return new TableSelector(getActorTableInfo(), filter, new Sort(getActorSortColumnName())).getArray(_actorClass); + } + + @Override + public A getActor(Container c, Object primaryKey) + { + return new TableSelector(getActorTableInfo()).getObject(primaryKey, _actorClass); + } + + @Override + public List getActorsByLabel(Container c, String label) + { + SimpleFilter filter = SimpleFilter.createContainerFilter(c); + filter.addCondition(FieldKey.fromParts("Label"), label); + return new TableSelector(getActorTableInfo(), filter, new Sort(getActorSortColumnName())).getArrayList(_actorClass); + } + + @Override + public Collection getActorsInUse(Container c) + { + Requirement[] requirements = getRequirements(c); + Map actors = new HashMap<>(); + for (Requirement requirement : requirements) + { + A actor = getActor(c, requirement.getActorPrimaryKey()); + actors.put(actor.getPrimaryKey(), actor); + } + Requirement[] defaultRequirements = getDefaultRequirements(c); + for (Requirement requirement : defaultRequirements) + { + A actor = getActor(c, requirement.getActorPrimaryKey()); + actors.put(actor.getPrimaryKey(), actor); + } + return actors.values(); + } + + @Override + public void purgeContainer(Container c) + { + R[] requirements = getRequirements(c); + for (R requirement : requirements) + requirement.delete(); + + A[] actors = getActors(c); + for (A actor : actors) + actor.delete(); + } + + + @Override + public void deleteRequirements(RequirementOwner owner) + { + R[] requirements = getRequirements(owner); + DbScope scope = SpecimenSchema.get().getScope(); + try (DbScope.Transaction transaction = scope.ensureTransaction()) + { + for (R requirement : requirements) + requirement.delete(); + + transaction.commit(); + } + } + + @Override + public R[] getRequirements(RequirementOwner owner) + { + return getRequirements(owner.getContainer(), owner.getEntityId()); + } + + @Override + public void generateDefaultRequirements(User user, RequirementOwner owner) + { + for (RequirementType type : getRequirementTypes()) + { + R[] defaultRequirements = getDefaultRequirements(owner.getContainer(), type); + for (R defaultRequirement : defaultRequirements) + { + List requirements = generateRequirementsFromDefault(owner, defaultRequirement, type); + for (R requirement : requirements) + createRequirement(user, owner, requirement); + } + } + } + + @Override + public R createRequirement(User user, RequirementOwner owner, R requirement) + { + return createRequirement(user, owner, requirement, false); + } + + + @Override + public R createRequirement(User user, RequirementOwner owner, R requirement, boolean forceDuplicate) + { + // unless we're forcing, we'll double-check to see if there's already an incomplete requirement matching this one: + if (!forceDuplicate) + { + for (R possibleMatch : getRequirements(owner)) + { + if (requirement.isEqual(possibleMatch)) + return null; + } + } + return requirement.persist(user, owner.getEntityId()); + } + + protected List generateRequirementsFromDefault(RequirementOwner owner, R defaultRequirement, RequirementType type) + { + return Collections.singletonList(defaultRequirement); + } + + protected abstract TableInfo getRequirementTableInfo(); + + protected abstract String getOwnerEntityIdColumnName(); + + protected abstract TableInfo getActorTableInfo(); + + protected abstract String getActorSortColumnName(); +} diff --git a/specimen/src/org/labkey/specimen/requirements/Requirement.java b/specimen/src/org/labkey/specimen/requirements/Requirement.java new file mode 100644 index 00000000..d9d8ba15 --- /dev/null +++ b/specimen/src/org/labkey/specimen/requirements/Requirement.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2008-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.specimen.requirements; + +import org.labkey.api.data.Container; +import org.labkey.api.security.User; + +/** + * User: brittp + * Date: Jun 4, 2007 + * Time: 2:31:52 PM + */ +public interface Requirement +{ + String getOwnerEntityId(); + + void setOwnerEntityId(String entityId); + + Container getContainer(); + + Object getActorPrimaryKey(); + + boolean isComplete(); + + R update(User user); + + R createMutable(); + + boolean isEqual(R requirement); + + void delete(); + + R persist(User user, String ownerEntityId); +} diff --git a/specimen/src/org/labkey/specimen/requirements/RequirementActor.java b/specimen/src/org/labkey/specimen/requirements/RequirementActor.java new file mode 100644 index 00000000..4073c3d5 --- /dev/null +++ b/specimen/src/org/labkey/specimen/requirements/RequirementActor.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2008-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.specimen.requirements; + +import org.labkey.api.data.Container; +import org.labkey.api.security.User; +import org.labkey.api.study.Location; + +/** + * User: brittp + * Date: Jun 4, 2007 + * Time: 2:56:54 PM + */ +public interface RequirementActor +{ + Object getPrimaryKey(); + + Container getContainer(); + + String getGroupName(); + + void addMembers(Location location, User... users); + + User[] getMembers(); + + User[] getMembers(Location location); + + void removeMembers(User... members); + + void removeMembers(Location location, User... members); + + void deleteAllGroups(); + + A create(User user); + + A update(User user); + + void delete(); +} \ No newline at end of file diff --git a/specimen/src/org/labkey/specimen/requirements/RequirementOwner.java b/specimen/src/org/labkey/specimen/requirements/RequirementOwner.java new file mode 100644 index 00000000..c471a785 --- /dev/null +++ b/specimen/src/org/labkey/specimen/requirements/RequirementOwner.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2008-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.specimen.requirements; + +import org.labkey.api.data.Container; + +/** + * User: brittp + * Date: Jun 4, 2007 + * Time: 2:31:59 PM + */ +public interface RequirementOwner +{ + String getEntityId(); + + Container getContainer(); +} diff --git a/specimen/src/org/labkey/specimen/requirements/RequirementProvider.java b/specimen/src/org/labkey/specimen/requirements/RequirementProvider.java new file mode 100644 index 00000000..52ae69ec --- /dev/null +++ b/specimen/src/org/labkey/specimen/requirements/RequirementProvider.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2008-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.specimen.requirements; + +import org.labkey.api.data.Container; +import org.labkey.api.security.User; + +import java.util.Collection; +import java.util.List; + +/** + * User: brittp + * Date: Jun 4, 2007 + * Time: 2:37:53 PM + */ +public interface RequirementProvider, + A extends RequirementActor> +{ + R getRequirement(Container container, Object requirementPrimaryKey); + + A[] getActors(Container c); + + A getActor(Container c, Object primaryKey); + + List getActorsByLabel(Container c, String label); + + Collection getActorsInUse(Container c); + + R[] getDefaultRequirements(Container container, RequirementType type); + + void generateDefaultRequirements(User user, RequirementOwner owner); + + void purgeContainer(Container c); + + R createDefaultRequirement(User user, R requirement, RequirementType type); + + RequirementType[] getRequirementTypes(); + + R createRequirement(User user, RequirementOwner owner, R requirement); + + R createRequirement(User user, RequirementOwner owner, R requirement, boolean forceDuplicate); + + void deleteRequirements(RequirementOwner owner); + + R[] getRequirements(RequirementOwner owner); +} diff --git a/specimen/src/org/labkey/specimen/requirements/RequirementType.java b/specimen/src/org/labkey/specimen/requirements/RequirementType.java new file mode 100644 index 00000000..646bb8ec --- /dev/null +++ b/specimen/src/org/labkey/specimen/requirements/RequirementType.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2008-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.specimen.requirements; + +/** + * User: brittp + * Date: Jun 4, 2007 + * Time: 3:21:36 PM + */ +public interface RequirementType +{ + String name(); +} diff --git a/specimen/src/org/labkey/specimen/requirements/SpecimenRequest.java b/specimen/src/org/labkey/specimen/requirements/SpecimenRequest.java new file mode 100644 index 00000000..c59f9fe7 --- /dev/null +++ b/specimen/src/org/labkey/specimen/requirements/SpecimenRequest.java @@ -0,0 +1,225 @@ +/* + * 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.specimen.requirements; + +import org.jetbrains.annotations.NotNull; +import org.labkey.api.data.Container; +import org.labkey.api.data.SQLFragment; +import org.labkey.api.data.SqlSelector; +import org.labkey.api.data.TableInfo; +import org.labkey.api.specimen.SpecimenSchema; +import org.labkey.api.specimen.Vial; +import org.labkey.api.specimen.location.LocationManager; +import org.labkey.api.study.AbstractStudyCachable; +import org.labkey.api.study.Location; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +/** + * User: brittp + * Date: Feb 8, 2006 + * Time: 1:50:53 PM + */ +public class SpecimenRequest extends AbstractStudyCachable implements RequirementOwner +{ + private Container _container; + private String _entityId; + private int _rowId; + private int _statusId; + private int _createdBy; + private long _created; + private int _modifiedBy; + private long _modified; + private String _comments; + private Integer _destinationSiteId; // This is a locationId, but still needs to martch the column in the table + private boolean _hidden; + + @Override + public Object getPrimaryKey() + { + return getRowId(); + } + + public int getRowId() + { + return _rowId; + } + + public void setRowId(int rowId) + { + verifyMutability(); + _rowId = rowId; + } + + public int getStatusId() + { + return _statusId; + } + + public void setStatusId(int statusId) + { + verifyMutability(); + _statusId = statusId; + } + + public String getComments() + { + return _comments; + } + + public void setComments(String comments) + { + verifyMutability(); + _comments = comments; + } + + public Integer getDestinationSiteId() + { + return _destinationSiteId; + } + + public void setDestinationSiteId(Integer destinationSiteId) + { + verifyMutability(); + _destinationSiteId = destinationSiteId; + } + + @NotNull + public List getVials() + { + final Container container = getContainer(); + SpecimenSchema specimenSchema = SpecimenSchema.get(); + TableInfo tableInfoSpecimen = specimenSchema.getTableInfoSpecimen(container); + TableInfo tableInfoVial = specimenSchema.getTableInfoVial(container); + SQLFragment sql = new SQLFragment("SELECT Specimens.*, Vial.*, ? As Container FROM "); + sql.add(container); + sql.append(specimenSchema.getTableInfoSampleRequest().getFromSQL("request")) + .append(", ").append(specimenSchema.getTableInfoSampleRequestSpecimen().getFromSQL("map")) + .append(", ").append(tableInfoSpecimen.getFromSQL("Specimens")) + .append(", ").append(tableInfoVial.getFromSQL("Vial")) + .append("\nWHERE request.RowId = map.SampleRequestId AND Vial.GlobalUniqueId = map.SpecimenGlobalUniqueId\n") + .append("AND Vial.SpecimenId = Specimens.RowId ") + .append("AND request.Container = map.Container AND map.Container = ? AND request.RowId = ?"); + sql.add(container); + sql.add(getRowId()); + + // TODO: LinkedList? + final List vials = new ArrayList<>(); + + new SqlSelector(specimenSchema.getSchema(), sql).forEachMap(map -> vials.add(new Vial(container, map))); + + return vials; + } + + public SpecimenRequestRequirement[] getRequirements() + { + return SpecimenRequestRequirementProvider.get().getRequirements(this); + } + + public boolean isHidden() + { + return _hidden; + } + + public void setHidden(boolean hidden) + { + verifyMutability(); + _hidden = hidden; + } + + @Override + public Container getContainer() + { + return _container; + } + + @Override + public void setContainer(Container container) + { + verifyMutability(); + _container = container; + } + + public Date getCreated() + { + return new Date(_created); + } + + public void setCreated(Date created) + { + verifyMutability(); + _created = created.getTime(); + } + + public int getCreatedBy() + { + return _createdBy; + } + + public void setCreatedBy(int createdBy) + { + verifyMutability(); + _createdBy = createdBy; + } + + public Date getModified() + { + return new Date(_modified); + } + + public void setModified(Date modified) + { + verifyMutability(); + _modified = modified.getTime(); + } + + public int getModifiedBy() + { + return _modifiedBy; + } + + public void setModifiedBy(int modifiedBy) + { + verifyMutability(); + _modifiedBy = modifiedBy; + } + + public String getRequestDescription() + { + StringBuilder builder = new StringBuilder(); + builder.append("Request ID ").append(_rowId); + if (_destinationSiteId != null) + { + Location destination = LocationManager.get().getLocation(_container, _destinationSiteId); + builder.append(", destination ").append(destination.getDisplayName()); + } + return builder.toString(); + } + + @Override + public String getEntityId() + { + return _entityId; + } + + public void setEntityId(String entityId) + { + _entityId = entityId; + } +} diff --git a/specimen/src/org/labkey/specimen/requirements/SpecimenRequestRequirement.java b/specimen/src/org/labkey/specimen/requirements/SpecimenRequestRequirement.java new file mode 100644 index 00000000..7b7d78ca --- /dev/null +++ b/specimen/src/org/labkey/specimen/requirements/SpecimenRequestRequirement.java @@ -0,0 +1,261 @@ +/* + * 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.specimen.requirements; + +import org.labkey.api.data.Container; +import org.labkey.api.data.TableInfo; +import org.labkey.api.specimen.SpecimenSchema; +import org.labkey.api.specimen.location.LocationImpl; +import org.labkey.api.specimen.location.LocationManager; +import org.labkey.api.study.StudyCachable; +import org.labkey.api.util.MemTracker; +import org.labkey.specimen.model.SpecimenRequestActor; + +/** + * User: brittp + * Date: Feb 8, 2006 + * Time: 4:18:29 PM + */ +public class SpecimenRequestRequirement extends DefaultRequirement + implements StudyCachable, Cloneable +{ + private int _rowId; + private Container _container; + private Integer _requestId; + private String _ownerEntityId; + private Integer _actorId; + private Integer _siteId; // This is a locationId, but must match column name in table + private boolean _complete; + private String _description; + private boolean _mutable = true; + + public SpecimenRequestRequirement() + { + MemTracker.getInstance().put(this); + } + + protected void verifyMutability() + { + if (!_mutable) + throw new IllegalStateException("Cached objects are immutable; createMutable must be called first."); + } + + public boolean isMutable() + { + return _mutable; + } + + protected void unlock() + { + _mutable = true; + } + + @Override + public void lock() + { + _mutable = false; + } + + + @Override + public SpecimenRequestRequirement createMutable() + { + try + { + SpecimenRequestRequirement obj = (SpecimenRequestRequirement) clone(); + obj.unlock(); + return obj; + } + catch (CloneNotSupportedException e) + { + throw new RuntimeException(e); + } + } + + public SpecimenRequestActor getActor() + { + if (_actorId == null) + return null; + return SpecimenRequestRequirementProvider.get().getActor(getContainer(), _actorId); + } + + public LocationImpl getLocation() + { + if (_siteId == null) + return null; + return LocationManager.get().getLocation(_container, _siteId); + } + + public Integer getActorId() + { + return _actorId; + } + + public void setActorId(Integer actorId) + { + verifyMutability(); + _actorId = actorId; + } + + @Override + public boolean isComplete() + { + return _complete; + } + + public void setComplete(boolean complete) + { + verifyMutability(); + _complete = complete; + } + + @Override + public Container getContainer() + { + return _container; + } + + public void setContainer(Container container) + { + verifyMutability(); + _container = container; + } + + public Integer getRequestId() + { + return _requestId; + } + + public void setRequestId(Integer requestId) + { + verifyMutability(); + _requestId = requestId; + } + + @Override + public Object getPrimaryKey() + { + return getRowId(); + } + + public int getRowId() + { + return _rowId; + } + + public void setRowId(int rowId) + { + verifyMutability(); + _rowId = rowId; + } + + public Integer getSiteId() + { + return _siteId; + } + + public void setSiteId(Integer siteId) + { + verifyMutability(); + _siteId = siteId; + } + + public String getDescription() + { + return _description; + } + + public void setDescription(String description) + { + _description = description; + } + + public String getRequirementSummary() + { + StringBuilder builder = new StringBuilder(); + if (_actorId != null) + builder.append(getActor().getLabel()); + if (_siteId != null) + builder.append(" (").append(getLocation().getLabel()).append(")"); + if (_description != null) + { + if (builder.length() > 0 && _description.length() > 0) + builder.append(", "); + builder.append(_description); + } + return builder.toString(); + } + + @Override + public String getOwnerEntityId() + { + return _ownerEntityId; + } + + @Override + public void setOwnerEntityId(String requestEntityId) + { + _ownerEntityId = requestEntityId; + } + + + @Override + public Object getActorPrimaryKey() + { + return _actorId; + } + + private int safeIntegerCompare(Integer first, Integer second) + { + if (first == null && second == null) + return 0; + if (first == null) + return -1; + if (second == null) + return 1; + return first.compareTo(second); + } + + @Override + public boolean isEqual(SpecimenRequestRequirement other) + { + if (this == other) + return true; + + int comp = safeIntegerCompare(getSiteId(), other.getSiteId()); + if (comp == 0) + comp = getDescription().compareTo(other.getDescription()); + if (comp == 0) + comp = safeIntegerCompare(getActorId(), other.getActorId()); + if (comp == 0) + comp = (isComplete() ? 1 : 0) - (other.isComplete() ? 1 : 0); + return comp == 0; + } + + + @Override + protected Object getPrimaryKeyValue() + { + return getRowId(); + } + + @Override + protected TableInfo getTableInfo() + { + return SpecimenSchema.get().getTableInfoSampleRequestRequirement(); + } +} diff --git a/specimen/src/org/labkey/specimen/requirements/SpecimenRequestRequirementProvider.java b/specimen/src/org/labkey/specimen/requirements/SpecimenRequestRequirementProvider.java new file mode 100644 index 00000000..4af4b5ab --- /dev/null +++ b/specimen/src/org/labkey/specimen/requirements/SpecimenRequestRequirementProvider.java @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2008-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.specimen.requirements; + +import org.labkey.api.data.Container; +import org.labkey.api.data.TableInfo; +import org.labkey.api.specimen.SpecimenSchema; +import org.labkey.specimen.model.SpecimenRequestActor; + +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * User: brittp + * Date: Jun 4, 2007 + * Time: 4:29:27 PM + */ +public class SpecimenRequestRequirementProvider extends DefaultRequirementProvider +{ + private static final SpecimenRequestRequirementProvider INSTANCE = new SpecimenRequestRequirementProvider(); + + public static SpecimenRequestRequirementProvider get() + { + return INSTANCE; + } + + private SpecimenRequestRequirementProvider() + { + super(SpecimenRequestRequirement.class, SpecimenRequestActor.class); + } + + @Override + public RequirementType[] getRequirementTypes() + { + return SpecimenRequestRequirementType.values(); + } + + @Override + protected String getOwnerEntityIdColumnName() + { + return "OwnerEntityId"; + } + + protected Object getPrimaryKeyValue(SpecimenRequestRequirement requirement) + { + return requirement.getRowId(); + } + + @Override + protected String getActorSortColumnName() + { + return "SortOrder"; + } + + @Override + protected TableInfo getActorTableInfo() + { + return SpecimenSchema.get().getTableInfoSampleRequestActor(); + } + + @Override + protected TableInfo getRequirementTableInfo() + { + return SpecimenSchema.get().getTableInfoSampleRequestRequirement(); + } + + protected SpecimenRequestRequirement createMutable(SpecimenRequestRequirement requirement) + { + return requirement.createMutable(); + } + + @Override + protected List generateRequirementsFromDefault(RequirementOwner owner, SpecimenRequestRequirement defaultRequirement, RequirementType type) + { + return ((SpecimenRequestRequirementType) type).generateRequirements((SpecimenRequest) owner, defaultRequirement); + } + + public Set getActorsInUseSet(Container container) + { + Collection actors = getActorsInUse(container); + Set ids = new HashSet<>(); + for (SpecimenRequestActor actor : actors) + ids.add(actor.getRowId()); + return ids; + } +} diff --git a/specimen/src/org/labkey/specimen/requirements/SpecimenRequestRequirementType.java b/specimen/src/org/labkey/specimen/requirements/SpecimenRequestRequirementType.java new file mode 100644 index 00000000..660fc013 --- /dev/null +++ b/specimen/src/org/labkey/specimen/requirements/SpecimenRequestRequirementType.java @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2008-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.specimen.requirements; + +import org.labkey.api.specimen.Vial; +import org.labkey.api.specimen.location.LocationImpl; +import org.labkey.api.specimen.location.LocationManager; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * User: brittp +* Date: Jun 7, 2007 +* Time: 11:26:49 AM +*/ +public enum SpecimenRequestRequirementType implements RequirementType +{ + ORIGINATING_SITE + { + @Override + public List generateRequirements(SpecimenRequest owner, SpecimenRequestRequirement defaultRequirement) + { + List requirements = new ArrayList<>(); + List vials = owner.getVials(); + if (vials.size() > 0) + { + // get a list of all providing and originating sites: + Set originatingLocationIds = new HashSet<>(); + for (Vial vial : vials) + { + LocationImpl originatingLocation = LocationManager.get().getOriginatingLocation(vial); + if (originatingLocation != null) + originatingLocationIds.add(originatingLocation.getRowId()); + } + for (Integer locationId : originatingLocationIds) + { + SpecimenRequestRequirement requirement = defaultRequirement.createMutable(); + requirement.setSiteId(locationId); + requirement.setRequestId(owner.getRowId()); + requirements.add(requirement); + } + } + return requirements; + } + }, + PROVIDING_SITE + { + @Override + public List generateRequirements(SpecimenRequest owner, SpecimenRequestRequirement defaultRequirement) + { + List requirements = new ArrayList<>(); + List vials = owner.getVials(); + if (vials.size() > 0) + { + // get a list of all providing and originating sites: + Set providerLocationIds = new HashSet<>(); + for (Vial vial : vials) + { + LocationImpl providingLocation = LocationManager.get().getCurrentLocation(vial); + if (providingLocation != null) + providerLocationIds.add(providingLocation.getRowId()); + } + for (Integer locationId : providerLocationIds) + { + SpecimenRequestRequirement requirement = defaultRequirement.createMutable(); + requirement.setRequestId(owner.getRowId()); + requirement.setSiteId(locationId); + requirements.add(requirement); + } + } + return requirements; + } + }, + RECEIVING_SITE + { + @Override + public List generateRequirements(SpecimenRequest owner, SpecimenRequestRequirement defaultRequirement) + { + if (owner.getDestinationSiteId() != null) + { + defaultRequirement.setSiteId(owner.getDestinationSiteId()); + defaultRequirement.setRequestId(owner.getRowId()); + return Collections.singletonList(defaultRequirement); + } + else + return Collections.emptyList(); + } + }, + NON_SITE_BASED + { + @Override + public List generateRequirements(SpecimenRequest owner, SpecimenRequestRequirement defaultRequirement) + { + defaultRequirement.setRequestId(owner.getRowId()); + return Collections.singletonList(defaultRequirement); + } + }; + + abstract public List generateRequirements(SpecimenRequest owner, SpecimenRequestRequirement defaultRequirement); +} diff --git a/specimen/src/org/labkey/specimen/security/permissions/ManageRequestsPermission.java b/specimen/src/org/labkey/specimen/security/permissions/ManageRequestsPermission.java new file mode 100644 index 00000000..667766b3 --- /dev/null +++ b/specimen/src/org/labkey/specimen/security/permissions/ManageRequestsPermission.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2009-2011 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.specimen.security.permissions; + +import org.labkey.api.security.permissions.AbstractPermission; +import org.labkey.api.study.StudyService; + +/* +* User: Dave +* Date: May 18, 2009 +* Time: 11:41:23 AM +*/ +public class ManageRequestsPermission extends AbstractPermission +{ + public ManageRequestsPermission() + { + super("Manage Specimen Requests", + "Allows management and approval of specimen requests", + StudyService.get().getStudyModuleClass()); // TODO: Switch to SpecimenModule.class + } +} \ No newline at end of file diff --git a/specimen/src/org/labkey/specimen/security/roles/SpecimenCoordinatorRole.java b/specimen/src/org/labkey/specimen/security/roles/SpecimenCoordinatorRole.java index 9a56d23c..e97f1aa6 100644 --- a/specimen/src/org/labkey/specimen/security/roles/SpecimenCoordinatorRole.java +++ b/specimen/src/org/labkey/specimen/security/roles/SpecimenCoordinatorRole.java @@ -21,9 +21,7 @@ import org.labkey.api.security.permissions.EditSharedViewPermission; import org.labkey.api.specimen.security.permissions.EditSpecimenDataPermission; import org.labkey.api.specimen.security.permissions.ManageRequestSettingsPermission; -import org.labkey.api.specimen.security.permissions.ManageRequestsPermission; import org.labkey.api.specimen.security.permissions.RequestSpecimensPermission; -import org.labkey.specimen.security.permissions.SetSpecimenCommentsPermission; import org.labkey.api.study.security.permissions.ManageStudyPermission; import org.labkey.specimen.security.permissions.LockSpecimensPermission; import org.labkey.specimen.security.permissions.ManageDisplaySettingsPermission; @@ -31,7 +29,9 @@ import org.labkey.specimen.security.permissions.ManageNotificationsPermission; import org.labkey.specimen.security.permissions.ManageRequestRequirementsPermission; import org.labkey.specimen.security.permissions.ManageRequestStatusesPermission; +import org.labkey.specimen.security.permissions.ManageRequestsPermission; import org.labkey.specimen.security.permissions.ManageSpecimenActorsPermission; +import org.labkey.specimen.security.permissions.SetSpecimenCommentsPermission; import java.util.Collection; import java.util.Set; diff --git a/specimen/src/org/labkey/specimen/view/NotificationBean.java b/specimen/src/org/labkey/specimen/view/NotificationBean.java new file mode 100644 index 00000000..e9254b6f --- /dev/null +++ b/specimen/src/org/labkey/specimen/view/NotificationBean.java @@ -0,0 +1,122 @@ +package org.labkey.specimen.view; + +import org.jetbrains.annotations.NotNull; +import org.labkey.api.attachments.Attachment; +import org.labkey.api.data.Container; +import org.labkey.api.security.User; +import org.labkey.api.specimen.SpecimenRequestStatus; +import org.labkey.api.specimen.location.LocationManager; +import org.labkey.api.study.Location; +import org.labkey.api.study.SpecimenUrls; +import org.labkey.api.util.PageFlowUtil; +import org.labkey.api.view.ActionURL; +import org.labkey.api.view.ViewContext; +import org.labkey.specimen.SpecimenRequestManager; +import org.labkey.specimen.model.SpecimenRequestEvent; +import org.labkey.specimen.notifications.DefaultRequestNotification; + +import java.util.List; + +public class NotificationBean +{ + private final DefaultRequestNotification _notification; + private final User _user; + private final String _specimenList; + private final String _studyName; + private final ActionURL _requestURL; + + private boolean _includeSpecimensInBody; + + public NotificationBean(ViewContext context, DefaultRequestNotification notification, String specimenList, String studyName) + { + _notification = notification; + _user = context.getUser(); + _specimenList = specimenList; + _studyName = studyName; + _requestURL = PageFlowUtil.urlProvider(SpecimenUrls.class).getManageRequestURL(context.getContainer(), getRequestId()); + + _includeSpecimensInBody = null != specimenList; + } + + public @NotNull List getAttachments() + { + return _notification.getAttachments(); + } + + public String getComments() + { + return _notification.getComments(); + } + + public int getRequestId() + { + return _notification.getSpecimenRequest().getRowId(); + } + + public String getModifyingUser() + { + return _user.getEmail(); + } + + public String getRequestingSiteName() + { + Location destLocation = LocationManager.get().getLocation(_notification.getSpecimenRequest().getContainer(), + _notification.getSpecimenRequest().getDestinationSiteId()); + if (destLocation != null) + return destLocation.getDisplayName(); + else + return null; + } + + public String getStatus() + { + SpecimenRequestStatus status = SpecimenRequestManager.get().getRequestStatus(_notification.getSpecimenRequest().getContainer(), + _notification.getSpecimenRequest().getStatusId()); + return status != null ? status.getLabel() : "Unknown"; + } + + public Container getContainer() + { + return _notification.getSpecimenRequest().getContainer(); + } + + public String getEventDescription() + { + return _notification.getEventSummary(); + } + + public String getRequestDescription() + { + return _notification.getSpecimenRequest().getComments(); + } + + public String getSpecimenList() + { + return _specimenList; + } + + public String getStudyName() + { + return _studyName; + } + + public ActionURL getRequestURL() + { + return _requestURL; + } + + public boolean getIncludeSpecimensInBody() + { + return _includeSpecimensInBody; + } + + public void setIncludeSpecimensInBody(boolean includeSpecimensInBody) + { + _includeSpecimensInBody = includeSpecimensInBody; + } + + public SpecimenRequestEvent getEvent() + { + return _notification.getEvent(); + } +} diff --git a/specimen/src/org/labkey/specimen/view/SpecimenRequestNotificationEmailTemplate.java b/specimen/src/org/labkey/specimen/view/SpecimenRequestNotificationEmailTemplate.java new file mode 100644 index 00000000..1109437a --- /dev/null +++ b/specimen/src/org/labkey/specimen/view/SpecimenRequestNotificationEmailTemplate.java @@ -0,0 +1,197 @@ +/* + * 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.specimen.view; + +import org.labkey.api.attachments.Attachment; +import org.labkey.api.data.Container; +import org.labkey.api.security.User; +import org.labkey.api.specimen.settings.RequestNotificationSettings; +import org.labkey.api.specimen.settings.SettingsManager; +import org.labkey.api.study.Study; +import org.labkey.api.study.StudyService; +import org.labkey.api.util.Link.LinkBuilder; +import org.labkey.api.util.PageFlowUtil; +import org.labkey.api.util.emailTemplate.EmailTemplate; +import org.labkey.specimen.actions.SpecimenController; + +import java.util.List; +import java.util.function.Function; + +/** + * User: jeckels + * Date: 11/11/13 + */ +public class SpecimenRequestNotificationEmailTemplate extends EmailTemplate +{ + public static final String NAME = "Specimen request notification"; + + protected static final String DEFAULT_SUBJECT = "^studyName^: ^subjectSuffix^"; + protected static final String DEFAULT_SENDER = "^userDisplayName^"; + + private String _subjectSuffix; + private NotificationBean _notification; + private User _originatingUser; + + private static String loadBody() + { + return + """ +
+
+ Specimen request #^specimenRequestNumber^ was ^simpleStatus^ in ^studyName^. +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + ^attachments|^ +
Request Details
Specimen Request^specimenRequestNumber^
Destination^destinationLocation^
Status^status^
Modified by^modifiedBy^
Action^action^
Attachments%s
+ ^comments|

Current Comments
%s

^ +

+ ^requestDescription^ +

+ ^specimenList^ + """; + } + + public SpecimenRequestNotificationEmailTemplate() + { + super(NAME, "Sent to users when a specimen request has been edited or inserted.", DEFAULT_SUBJECT, loadBody(), ContentType.HTML, Scope.SiteOrFolder); + setSenderName(DEFAULT_SENDER); + } + + /** + * Evaluates to null if there's no associated specimen request (as there would be in the admin preview mode), + * otherwise delegates to subclass to determine the actual value. + */ + private final class NotificationReplacement extends ReplacementParam + { + private final Function _valueGetter; + + public NotificationReplacement(String name, Class valueType, String description, Function valueGetter) + { + super(name, valueType, description, ContentType.Plain); + _valueGetter = valueGetter; + } + + public NotificationReplacement(String name, Class valueType, String description, ContentType contentType, Function valueGetter) + { + super(name, valueType, description, contentType); + _valueGetter = valueGetter; + } + + @Override + public Type getValue(Container c) + { + // No request available, so can't come up with an actual value + if (_notification == null) + { + return null; + } + + return _valueGetter.apply(c); + } + } + + public void init(NotificationBean notification) + { + _notification = notification; + + // For backwards compatibility, use the template specified in the notification management UI + RequestNotificationSettings settings = + SettingsManager.get().getRequestNotificationSettings(notification.getContainer()); + + _subjectSuffix = settings.getSubjectSuffix().replaceAll("%requestId%", "" + notification.getRequestId()); + } + + public void setOriginatingUser(User user){_originatingUser = user;} + + @Override + protected void addCustomReplacements(Replacements replacements) + { + replacements.add("studyName", String.class, "The name of this folder's study", ContentType.Plain, c -> { + Study study = StudyService.get().getStudy(c); + return study == null ? "" : study.getLabel(); + }); + replacements.add("subjectSuffix", String.class, "Email subject suffix, configured in specimen request notification settings", ContentType.Plain, c -> _subjectSuffix); + replacements.add(new NotificationReplacement<>("specimenRequestNumber", Integer.class, "Unique, auto-incrementing number for this request", c -> _notification.getRequestId())); + replacements.add(new NotificationReplacement<>("destinationLocation", String.class, "The location to which the specimen is to be sent", c -> _notification.getRequestingSiteName())); + replacements.add(new NotificationReplacement<>("status", String.class, "The status of the request (submitted, approved, etc)", c -> _notification.getStatus())); + replacements.add(new NotificationReplacement<>("simpleStatus", String.class, "Either 'submitted' for brand new requests, or 'updated' for all other changes", c -> "submitted".equalsIgnoreCase(_notification.getStatus()) ? "submitted" : "updated")); + replacements.add(new NotificationReplacement<>("modifiedBy", String.class, "The user who created or modified the request", c -> _notification.getModifyingUser())); + replacements.add(new NotificationReplacement<>("action", String.class, "A description of what action was performed, such as 'New Request Created'", c -> _notification.getEventDescription())); + replacements.add(new NotificationReplacement<>("attachments", String.class, "A list of direct links to all the attachments associated with the request", ContentType.HTML, c -> { + List attachments = _notification.getAttachments(); + if (attachments.isEmpty()) + { + return null; + } + StringBuilder sb = new StringBuilder(); + for (Attachment att : attachments) + { + sb.append(new LinkBuilder(att.getName()).href(SpecimenController.getDownloadURL(_notification.getEvent(), att.getName())).clearClasses()); + sb.append("
"); + } + return sb.toString(); + })); + replacements.add(new NotificationReplacement<>("specimenList", String.class, "A list of the specimens in the request, as configured in the Specimen Notification settings", ContentType.HTML, c -> { + if (_notification.getSpecimenList() != null) + { + StringBuilder sb = new StringBuilder("

"); + sb.append("Specimen List (Request Link)

\n"); + if (_notification.getIncludeSpecimensInBody()) + { + sb.append(_notification.getSpecimenList()); + } + sb.append("

"); + return sb.toString(); + } + return null; + })); + replacements.add(new NotificationReplacement<>("requestDescription", String.class, "Typically includes assay plan, shipping information, etc", c -> _notification.getRequestDescription())); + replacements.add(new NotificationReplacement<>("comments", String.class, "Text submitter wrote in Comments text box, or '[Not provided]'", c -> { + String comments = _notification.getComments(); + return comments == null ? "[Not provided]" : comments; + })); + replacements.add("userFirstName", String.class, "First name of the user performing the operation, only works when reply to current user", ContentType.Plain, c -> _originatingUser == null ? null : _originatingUser.getFirstName()); + replacements.add("userLastName", String.class, "Last name of the user performing the operation, only works when reply to current user", ContentType.Plain, c -> _originatingUser == null ? null : _originatingUser.getLastName()); + replacements.add("userDisplayName", String.class, "Display name of the user performing the operation, only works when reply to current user", ContentType.Plain, c -> _originatingUser == null ? null : _originatingUser.getFriendlyName()); + } +} diff --git a/specimen/src/org/labkey/specimen/view/manageActorOrder.jsp b/specimen/src/org/labkey/specimen/view/manageActorOrder.jsp index 25a5e585..af80e614 100644 --- a/specimen/src/org/labkey/specimen/view/manageActorOrder.jsp +++ b/specimen/src/org/labkey/specimen/view/manageActorOrder.jsp @@ -15,10 +15,10 @@ * limitations under the License. */ %> -<%@ page import="org.labkey.api.specimen.model.SpecimenRequestActor"%> -<%@ page import="org.labkey.api.specimen.requirements.SpecimenRequestRequirementProvider"%> -<%@ page import="org.labkey.specimen.actions.SpecimenController.ManageActorOrderAction" %> -<%@ page import="org.labkey.specimen.actions.SpecimenController.ManageActorsAction" %> +<%@ page import="org.labkey.specimen.actions.SpecimenController.ManageActorOrderAction"%> +<%@ page import="org.labkey.specimen.actions.SpecimenController.ManageActorsAction"%> +<%@ page import="org.labkey.specimen.model.SpecimenRequestActor" %> +<%@ page import="org.labkey.specimen.requirements.SpecimenRequestRequirementProvider" %> <%@ taglib prefix="labkey" uri="http://www.labkey.org/taglib" %> <%@ page extends="org.labkey.api.jsp.JspBase" %> <% diff --git a/specimen/src/org/labkey/specimen/view/manageActors.jsp b/specimen/src/org/labkey/specimen/view/manageActors.jsp index 8ef5bfe2..07d24768 100644 --- a/specimen/src/org/labkey/specimen/view/manageActors.jsp +++ b/specimen/src/org/labkey/specimen/view/manageActors.jsp @@ -17,17 +17,17 @@ %> <%@ page import="org.labkey.api.specimen.location.LocationImpl" %> <%@ page import="org.labkey.api.specimen.location.LocationManager" %> -<%@ page import="org.labkey.api.specimen.model.SpecimenRequestActor" %> -<%@ page import="org.labkey.api.specimen.requirements.SpecimenRequestRequirementProvider" %> -<%@ page import="org.labkey.api.study.Study"%> +<%@ page import="org.labkey.api.study.Study" %> <%@ page import="org.labkey.api.study.StudyUrls" %> -<%@ page import="org.labkey.api.view.ActionURL" %> +<%@ 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.specimen.actions.ShowGroupMembersAction" %> <%@ page import="org.labkey.specimen.actions.SpecimenController.DeleteActorAction" %> <%@ page import="org.labkey.specimen.actions.SpecimenController.ManageActorOrderAction" %> <%@ page import="org.labkey.specimen.actions.SpecimenController.ManageActorsAction" %> +<%@ page import="org.labkey.specimen.model.SpecimenRequestActor" %> +<%@ page import="org.labkey.specimen.requirements.SpecimenRequestRequirementProvider" %> <%@ page import="java.util.Set" %> <%@ taglib prefix="labkey" uri="http://www.labkey.org/taglib" %> <%@ page extends="org.labkey.api.jsp.JspBase" %> diff --git a/specimen/src/org/labkey/specimen/view/manageDefaultReqs.jsp b/specimen/src/org/labkey/specimen/view/manageDefaultReqs.jsp index 9f27408e..2e71ec62 100644 --- a/specimen/src/org/labkey/specimen/view/manageDefaultReqs.jsp +++ b/specimen/src/org/labkey/specimen/view/manageDefaultReqs.jsp @@ -15,16 +15,16 @@ * limitations under the License. */ %> -<%@ page import="org.labkey.api.specimen.model.SpecimenRequestActor" %> -<%@ page import="org.labkey.api.specimen.requirements.SpecimenRequestRequirement" %> <%@ 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.HttpView" %> -<%@ page import="org.labkey.api.view.JspView" %> +<%@ page import="org.labkey.api.util.HtmlString" %> +<%@ 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.specimen.actions.ManageReqsBean" %> <%@ page import="org.labkey.specimen.actions.SpecimenController" %> <%@ page import="org.labkey.specimen.actions.SpecimenController.ManageDefaultReqsAction" %> +<%@ page import="org.labkey.specimen.model.SpecimenRequestActor" %> +<%@ page import="org.labkey.specimen.requirements.SpecimenRequestRequirement" %> <%@ taglib prefix="labkey" uri="http://www.labkey.org/taglib" %> <%@ page extends="org.labkey.api.jsp.JspBase" %> <% diff --git a/specimen/src/org/labkey/specimen/view/manageNotifications.jsp b/specimen/src/org/labkey/specimen/view/manageNotifications.jsp index 2ec66bf6..3a56e5d3 100644 --- a/specimen/src/org/labkey/specimen/view/manageNotifications.jsp +++ b/specimen/src/org/labkey/specimen/view/manageNotifications.jsp @@ -23,7 +23,6 @@ <%@ page import="org.labkey.api.specimen.settings.RequestNotificationSettings" %> <%@ page import="org.labkey.api.specimen.settings.RequestNotificationSettings.DefaultEmailNotifyEnum" %> <%@ page import="org.labkey.api.specimen.settings.RequestNotificationSettings.SpecimensAttachmentEnum" %> -<%@ page import="org.labkey.api.specimen.view.SpecimenRequestNotificationEmailTemplate" %> <%@ page import="org.labkey.api.study.StudyService" %> <%@ page import="org.labkey.api.study.StudyUrls" %> <%@ page import="org.labkey.api.util.HtmlString" %> @@ -31,6 +30,7 @@ <%@ page import="org.labkey.api.view.HttpView" %> <%@ page import="org.labkey.api.view.JspView" %> <%@ page import="org.labkey.specimen.actions.SpecimenController.ManageNotificationsAction" %> +<%@ page import="org.labkey.specimen.view.SpecimenRequestNotificationEmailTemplate" %> <%@ page extends="org.labkey.api.jsp.JspBase" %> <%@ taglib prefix="labkey" uri="http://www.labkey.org/taglib" %> <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> diff --git a/specimen/src/org/labkey/specimen/view/manageRequest.jsp b/specimen/src/org/labkey/specimen/view/manageRequest.jsp index 57ef1cc7..17692462 100644 --- a/specimen/src/org/labkey/specimen/view/manageRequest.jsp +++ b/specimen/src/org/labkey/specimen/view/manageRequest.jsp @@ -20,14 +20,10 @@ <%@ page import="org.labkey.api.data.ContainerManager"%> <%@ page import="org.labkey.api.security.User"%> <%@ page import="org.labkey.api.security.UserManager"%> -<%@ page import="org.labkey.api.specimen.SpecimenRequestManager"%> -<%@ page import="org.labkey.api.specimen.SpecimenRequestStatus" %> -<%@ page import="org.labkey.api.specimen.Vial"%> -<%@ page import="org.labkey.api.specimen.location.LocationImpl" %> +<%@ page import="org.labkey.api.specimen.SpecimenRequestStatus"%> +<%@ page import="org.labkey.api.specimen.Vial" %> +<%@ page import="org.labkey.api.specimen.location.LocationImpl"%> <%@ page import="org.labkey.api.specimen.location.LocationManager" %> -<%@ page import="org.labkey.api.specimen.model.SpecimenRequestActor" %> -<%@ page import="org.labkey.api.specimen.requirements.SpecimenRequestRequirement" %> -<%@ page import="org.labkey.api.specimen.requirements.SpecimenRequestRequirementProvider" %> <%@ page import="org.labkey.api.specimen.settings.SettingsManager" %> <%@ page import="org.labkey.api.study.Location" %> <%@ page import="org.labkey.api.study.SpecimenService" %> @@ -39,7 +35,7 @@ <%@ page import="org.labkey.api.view.JspView" %> <%@ page import="org.labkey.api.view.template.ClientDependencies" %> <%@ page import="org.labkey.specimen.SpecimenManager" %> -<%@ page import="org.labkey.specimen.SpecimenModule" %> +<%@ page import="org.labkey.specimen.SpecimenRequestManager" %> <%@ page import="org.labkey.specimen.actions.ManageRequestBean" %> <%@ page import="org.labkey.specimen.actions.ShowSearchAction" %> <%@ page import="org.labkey.specimen.actions.SpecimenController" %> @@ -53,6 +49,9 @@ <%@ page import="org.labkey.specimen.actions.SpecimenController.ManageRequirementAction" %> <%@ page import="org.labkey.specimen.actions.SpecimenController.RequestHistoryAction" %> <%@ page import="org.labkey.specimen.actions.SpecimenController.SubmitRequestAction" %> +<%@ page import="org.labkey.specimen.model.SpecimenRequestActor" %> +<%@ page import="org.labkey.specimen.requirements.SpecimenRequestRequirement" %> +<%@ page import="org.labkey.specimen.requirements.SpecimenRequestRequirementProvider" %> <%@ page import="java.util.Arrays" %> <%@ page import="java.util.List" %> <%@ taglib prefix="labkey" uri="http://www.labkey.org/taglib" %> @@ -63,7 +62,6 @@ { dependencies.add("clientapi/ext3"); dependencies.add("FileUploadField.js"); - dependencies.add("study/StudyWizard.js"); } %> <% @@ -184,23 +182,6 @@ LABKEY.Utils.setCookie("selectedRequest", requestId, true); } - function showNewStudyWizard() - { - Ext.onReady(function(){ - var wizard = new LABKEY.study.CreateStudyWizard({ - mode : 'specimen', - allowRefresh : false, - requestId : <%=bean.getSpecimenRequest().getRowId()%>, - studyName : <%=q(availableStudyName)%> - }); - - wizard.on('success', function(info){}, this); - - // run the wizard - wizard.show(); - }); - } - <% @@ -342,14 +323,6 @@ You must add specimens before submitting your request.

<%=specimenSearchButton%> <%=importVialIdsButton%> -<% - } - - // TODO: Remove this and all associated code - if (false) - { - String ignored = SpecimenModule.CREATE_SPECIMEN_STUDY; -%> <%= button("Create Study").href("javascript:void(0)").onClick("showNewStudyWizard();") %> <% } %> diff --git a/specimen/src/org/labkey/specimen/view/manageRequestInputs.jsp b/specimen/src/org/labkey/specimen/view/manageRequestInputs.jsp index 0ac74bd1..8b185ecd 100644 --- a/specimen/src/org/labkey/specimen/view/manageRequestInputs.jsp +++ b/specimen/src/org/labkey/specimen/view/manageRequestInputs.jsp @@ -15,10 +15,10 @@ * limitations under the License. */ %> -<%@ page import="org.labkey.api.specimen.SpecimenRequestManager.SpecimenRequestInput"%> <%@ page import="org.labkey.api.study.StudyUrls"%> <%@ page import="org.labkey.api.view.HttpView"%> <%@ page import="org.labkey.api.view.JspView"%> +<%@ page import="org.labkey.specimen.SpecimenRequestManager.SpecimenRequestInput"%> <%@ page import="org.labkey.specimen.actions.ManageRequestInputsBean"%> <%@ page import="org.labkey.specimen.actions.SpecimenController.HandleUpdateRequestInputsAction" %> <%@ taglib prefix="labkey" uri="http://www.labkey.org/taglib" %> diff --git a/specimen/src/org/labkey/specimen/view/manageRequestStatus.jsp b/specimen/src/org/labkey/specimen/view/manageRequestStatus.jsp index 4dacb718..a5706937 100644 --- a/specimen/src/org/labkey/specimen/view/manageRequestStatus.jsp +++ b/specimen/src/org/labkey/specimen/view/manageRequestStatus.jsp @@ -15,11 +15,11 @@ * limitations under the License. */ %> -<%@ page import="org.labkey.api.specimen.SpecimenRequestManager" %> <%@ page import="org.labkey.api.specimen.SpecimenRequestStatus" %> -<%@ page import="org.labkey.api.study.SpecimenService"%> +<%@ page import="org.labkey.api.study.SpecimenService" %> <%@ page import="org.labkey.api.view.HttpView"%> <%@ page import="org.labkey.api.view.JspView"%> +<%@ page import="org.labkey.specimen.SpecimenRequestManager"%> <%@ page import="org.labkey.specimen.actions.ManageRequestBean" %> <%@ page import="org.labkey.specimen.actions.SpecimenController" %> <%@ page import="org.labkey.specimen.notifications.ActorNotificationRecipientSet" %> diff --git a/specimen/src/org/labkey/specimen/view/manageRequirement.jsp b/specimen/src/org/labkey/specimen/view/manageRequirement.jsp index 17584fb1..954074a9 100644 --- a/specimen/src/org/labkey/specimen/view/manageRequirement.jsp +++ b/specimen/src/org/labkey/specimen/view/manageRequirement.jsp @@ -15,17 +15,17 @@ * limitations under the License. */ %> -<%@ page import="org.labkey.api.specimen.requirements.SpecimenRequestRequirement"%> <%@ page import="org.labkey.api.study.Location"%> <%@ 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.api.view.template.ClientDependencies"%> <%@ page import="org.labkey.specimen.actions.SpecimenController" %> <%@ page import="org.labkey.specimen.actions.SpecimenController.DeleteRequirementAction" %> <%@ page import="org.labkey.specimen.actions.SpecimenController.ManageRequirementAction" %> <%@ page import="org.labkey.specimen.actions.SpecimenController.ManageRequirementBean" %> <%@ page import="org.labkey.specimen.notifications.ActorNotificationRecipientSet" %> +<%@ page import="org.labkey.specimen.requirements.SpecimenRequestRequirement" %> <%@ page import="java.util.List" %> <%@ page extends="org.labkey.api.jsp.JspBase" %> <%@ taglib prefix="labkey" uri="http://www.labkey.org/taglib" %> diff --git a/specimen/src/org/labkey/specimen/view/manageSpecimens.jsp b/specimen/src/org/labkey/specimen/view/manageSpecimens.jsp index 0a11aa7b..cfdbb4c0 100644 --- a/specimen/src/org/labkey/specimen/view/manageSpecimens.jsp +++ b/specimen/src/org/labkey/specimen/view/manageSpecimens.jsp @@ -20,9 +20,7 @@ <%@ page import="org.labkey.api.exp.property.Domain" %> <%@ page import="org.labkey.api.security.User" %> <%@ page import="org.labkey.api.security.permissions.AdminPermission" %> -<%@ page import="org.labkey.api.specimen.SpecimenRequestManager" %> <%@ page import="org.labkey.api.specimen.model.SpecimenTablesProvider" %> -<%@ page import="org.labkey.api.specimen.requirements.SpecimenRequestRequirementProvider" %> <%@ page import="org.labkey.api.specimen.security.permissions.ManageRequestSettingsPermission" %> <%@ page import="org.labkey.api.specimen.settings.SettingsManager" %> <%@ page import="org.labkey.api.study.SpecimenService" %> @@ -30,6 +28,7 @@ <%@ page import="org.labkey.api.study.Study" %> <%@ page import="org.labkey.api.study.StudyService" %> <%@ page import="org.labkey.api.view.ActionURL" %> +<%@ page import="org.labkey.specimen.SpecimenRequestManager" %> <%@ page import="org.labkey.specimen.actions.SpecimenController.ChooseImporterAction" %> <%@ page import="org.labkey.specimen.actions.SpecimenController.ConfigureRequestabilityRulesAction" %> <%@ page import="org.labkey.specimen.actions.SpecimenController.ManageActorsAction" %> @@ -42,6 +41,7 @@ <%@ page import="org.labkey.specimen.actions.SpecimenController.ManageSpecimenCommentsAction" %> <%@ page import="org.labkey.specimen.actions.SpecimenController.ManageSpecimenWebPartAction" %> <%@ page import="org.labkey.specimen.actions.SpecimenController.ManageStatusesAction" %> +<%@ page import="org.labkey.specimen.requirements.SpecimenRequestRequirementProvider" %> <%@ page import="java.util.Collection" %> <%@ page extends="org.labkey.api.jsp.JspBase" %> <%@ taglib prefix="labkey" uri="http://www.labkey.org/taglib" %> diff --git a/specimen/src/org/labkey/specimen/view/manageStatusOrder.jsp b/specimen/src/org/labkey/specimen/view/manageStatusOrder.jsp index 6b409315..e22804c7 100644 --- a/specimen/src/org/labkey/specimen/view/manageStatusOrder.jsp +++ b/specimen/src/org/labkey/specimen/view/manageStatusOrder.jsp @@ -15,15 +15,14 @@ * limitations under the License. */ %> -<%@ page import="org.labkey.api.specimen.SpecimenRequestManager"%> <%@ page import="org.labkey.api.specimen.SpecimenRequestStatus"%> <%@ page import="org.labkey.api.study.Study"%> <%@ page import="org.labkey.api.view.HttpView"%> <%@ page import="org.labkey.api.view.JspView"%> -<%@ page import="java.util.List" %> -<%@ page import="org.labkey.specimen.actions.SpecimenController" %> +<%@ page import="org.labkey.specimen.SpecimenRequestManager"%> <%@ page import="org.labkey.specimen.actions.SpecimenController.ManageStatusOrderAction" %> <%@ page import="org.labkey.specimen.actions.SpecimenController.ManageStatusesAction" %> +<%@ page import="java.util.List" %> <%@ taglib prefix="labkey" uri="http://www.labkey.org/taglib" %> <%@ page extends="org.labkey.api.jsp.JspBase" %> <% diff --git a/specimen/src/org/labkey/specimen/view/manageStatuses.jsp b/specimen/src/org/labkey/specimen/view/manageStatuses.jsp index 98611899..bc699266 100644 --- a/specimen/src/org/labkey/specimen/view/manageStatuses.jsp +++ b/specimen/src/org/labkey/specimen/view/manageStatuses.jsp @@ -15,21 +15,20 @@ * limitations under the License. */ %> -<%@ page import="org.labkey.api.specimen.SpecimenRequestManager"%> <%@ page import="org.labkey.api.specimen.SpecimenRequestStatus"%> <%@ page import="org.labkey.api.specimen.settings.SettingsManager"%> <%@ page import="org.labkey.api.specimen.settings.StatusSettings"%> <%@ page import="org.labkey.api.study.Study"%> -<%@ page import="org.labkey.api.study.StudyUrls" %> +<%@ 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="java.util.List" %> -<%@ page import="java.util.Set" %> -<%@ page import="org.labkey.specimen.actions.SpecimenController" %> +<%@ page import="org.labkey.specimen.SpecimenRequestManager" %> <%@ page import="org.labkey.specimen.actions.SpecimenController.DeleteStatusAction" %> -<%@ page import="org.labkey.specimen.actions.SpecimenController.ManageStatusesAction" %> <%@ page import="org.labkey.specimen.actions.SpecimenController.ManageStatusOrderAction" %> +<%@ page import="org.labkey.specimen.actions.SpecimenController.ManageStatusesAction" %> +<%@ page import="java.util.List" %> +<%@ page import="java.util.Set" %> <%@ taglib prefix="labkey" uri="http://www.labkey.org/taglib" %> <%@ page extends="org.labkey.api.jsp.JspBase" %> <% diff --git a/specimen/src/org/labkey/specimen/view/requestSpecimens.jsp b/specimen/src/org/labkey/specimen/view/requestSpecimens.jsp index 23cf03e4..bccf7174 100644 --- a/specimen/src/org/labkey/specimen/view/requestSpecimens.jsp +++ b/specimen/src/org/labkey/specimen/view/requestSpecimens.jsp @@ -16,18 +16,18 @@ */ %> <%@ page import="org.labkey.api.data.Container"%> -<%@ page import="org.labkey.api.specimen.SpecimenRequestManager.SpecimenRequestInput"%> <%@ page import="org.labkey.api.specimen.Vial"%> <%@ page import="org.labkey.api.specimen.location.LocationImpl"%> <%@ page import="org.labkey.api.specimen.location.LocationManager"%> <%@ page import="org.labkey.api.specimen.settings.SettingsManager"%> <%@ page import="org.labkey.api.study.SpecimenService"%> <%@ page import="org.labkey.api.view.ActionURL"%> -<%@ page import="org.labkey.api.view.HttpView" %> +<%@ page import="org.labkey.api.view.HttpView"%> <%@ page import="org.labkey.api.view.JspView" %> <%@ page import="org.labkey.api.view.ViewContext" %> <%@ page import="org.labkey.api.view.template.ClientDependencies" %> <%@ page import="org.labkey.specimen.SpecimenManager" %> +<%@ page import="org.labkey.specimen.SpecimenRequestManager.SpecimenRequestInput" %> <%@ page import="org.labkey.specimen.actions.SpecimenController.CreateSpecimenRequestForm" %> <%@ page import="org.labkey.specimen.actions.SpecimenController.HandleCreateSpecimenRequestAction" %> <%@ page import="org.labkey.specimen.actions.SpecimenController.NewRequestBean" %> diff --git a/specimen/src/org/labkey/specimen/writer/SpecimenArchiveDataTypes.java b/specimen/src/org/labkey/specimen/writer/SpecimenArchiveDataTypes.java new file mode 100644 index 00000000..3d617466 --- /dev/null +++ b/specimen/src/org/labkey/specimen/writer/SpecimenArchiveDataTypes.java @@ -0,0 +1,14 @@ +package org.labkey.specimen.writer; + +import org.labkey.api.specimen.SpecimenMigrationService; + +public class SpecimenArchiveDataTypes +{ + public static final String SCHEMA_FILENAME = "specimens_metadata.xml"; + public static final String SPECIMEN_SETTINGS = "Specimen Settings"; + public static final String SPECIMENS = SpecimenMigrationService.SPECIMENS_ARCHIVE_TYPE; + + private SpecimenArchiveDataTypes() + { + } +} diff --git a/specimen/src/org/labkey/specimen/writer/SpecimenArchiveWriter.java b/specimen/src/org/labkey/specimen/writer/SpecimenArchiveWriter.java index 37414db3..dc844fd4 100644 --- a/specimen/src/org/labkey/specimen/writer/SpecimenArchiveWriter.java +++ b/specimen/src/org/labkey/specimen/writer/SpecimenArchiveWriter.java @@ -30,8 +30,6 @@ import org.labkey.api.specimen.SpecimenColumns; import org.labkey.api.specimen.SpecimenSchema; import org.labkey.api.specimen.model.SpecimenTablesProvider; -import org.labkey.api.specimen.writer.SpecimenArchiveDataTypes; -import org.labkey.specimen.writer.StandardSpecimenWriter.QueryInfo; import org.labkey.api.study.Study; import org.labkey.api.study.writer.SimpleStudyExportContext; import org.labkey.api.writer.VirtualFile; @@ -39,6 +37,7 @@ import org.labkey.data.xml.TableType; import org.labkey.data.xml.TablesDocument; import org.labkey.data.xml.TablesType; +import org.labkey.specimen.writer.StandardSpecimenWriter.QueryInfo; import org.labkey.study.xml.StudyDocument; import java.util.ArrayList; diff --git a/specimen/src/org/labkey/specimen/writer/SpecimenSettingsWriter.java b/specimen/src/org/labkey/specimen/writer/SpecimenSettingsWriter.java index e9ad5c6c..0c8978b6 100644 --- a/specimen/src/org/labkey/specimen/writer/SpecimenSettingsWriter.java +++ b/specimen/src/org/labkey/specimen/writer/SpecimenSettingsWriter.java @@ -17,26 +17,25 @@ import org.jetbrains.annotations.Nullable; import org.labkey.api.security.GroupManager; -import org.labkey.api.specimen.SpecimenRequestManager; -import org.labkey.api.specimen.SpecimenRequestManager.SpecimenRequestInput; import org.labkey.api.specimen.SpecimenRequestStatus; -import org.labkey.specimen.actions.ManageReqsBean; import org.labkey.api.specimen.importer.RequestabilityManager; -import org.labkey.api.specimen.model.SpecimenRequestActor; -import org.labkey.api.specimen.requirements.SpecimenRequestRequirement; -import org.labkey.api.specimen.requirements.SpecimenRequestRequirementProvider; import org.labkey.api.specimen.settings.DisplaySettings; import org.labkey.api.specimen.settings.RepositorySettings; import org.labkey.api.specimen.settings.RequestNotificationSettings; import org.labkey.api.specimen.settings.SettingsManager; import org.labkey.api.specimen.settings.StatusSettings; -import org.labkey.api.specimen.writer.SpecimenArchiveDataTypes; import org.labkey.api.study.Location; import org.labkey.api.study.Study; import org.labkey.api.study.writer.SimpleStudyExportContext; import org.labkey.api.writer.VirtualFile; import org.labkey.security.xml.GroupType; import org.labkey.security.xml.GroupsType; +import org.labkey.specimen.SpecimenRequestManager; +import org.labkey.specimen.SpecimenRequestManager.SpecimenRequestInput; +import org.labkey.specimen.actions.ManageReqsBean; +import org.labkey.specimen.model.SpecimenRequestActor; +import org.labkey.specimen.requirements.SpecimenRequestRequirement; +import org.labkey.specimen.requirements.SpecimenRequestRequirementProvider; import org.labkey.study.xml.DefaultRequirementType; import org.labkey.study.xml.DefaultRequirementsType; import org.labkey.study.xml.SpecimenRepositoryType; diff --git a/specimen/src/org/labkey/specimen/writer/SpecimenWriter.java b/specimen/src/org/labkey/specimen/writer/SpecimenWriter.java index 7c39f447..e5afeacf 100644 --- a/specimen/src/org/labkey/specimen/writer/SpecimenWriter.java +++ b/specimen/src/org/labkey/specimen/writer/SpecimenWriter.java @@ -35,7 +35,6 @@ import org.labkey.api.query.UserSchema; import org.labkey.api.specimen.SpecimenSchema; import org.labkey.api.specimen.SpecimenTableManager; -import org.labkey.api.specimen.Vial; import org.labkey.api.specimen.importer.SpecimenColumn; import org.labkey.api.specimen.importer.TargetTable; import org.labkey.api.study.Study; @@ -46,7 +45,6 @@ import java.io.PrintWriter; import java.util.ArrayList; import java.util.Collection; -import java.util.LinkedList; import java.util.List; /** @@ -209,20 +207,6 @@ else if (null == column.getFkColumn()) conjunction = " AND "; } - if (null != ctx.getVials() && !ctx.getVials().isEmpty()) - { - List vials = ctx.getVials(); - List uniqueIds = new LinkedList<>(); - - for (Vial vial : vials) - uniqueIds.add(vial.getGlobalUniqueId()); - - sql.append(conjunction).append("\n s.GlobalUniqueId IN ("); - sql.append(convertListToString(uniqueIds, true)); - sql.append(")"); - conjunction = " AND"; - } - sql.append("\nORDER BY se.ExternalId"); // Note: must be uncached result set -- this query can be very large From 5c4708340faa6495821518a74a1fe0e79b838640 Mon Sep 17 00:00:00 2001 From: Karl Lum Date: Tue, 15 Mar 2022 09:22:35 -0700 Subject: [PATCH 361/494] 45034: CIML's BSIImport module failing due to S3 bug from 21.11 Original PR: https://github.com/LabKey/platform/pull/3137 --- .../org/labkey/specimen/pipeline/FileAnalysisSpecimenTask.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/specimen/src/org/labkey/specimen/pipeline/FileAnalysisSpecimenTask.java b/specimen/src/org/labkey/specimen/pipeline/FileAnalysisSpecimenTask.java index 560dc029..b1892de1 100644 --- a/specimen/src/org/labkey/specimen/pipeline/FileAnalysisSpecimenTask.java +++ b/specimen/src/org/labkey/specimen/pipeline/FileAnalysisSpecimenTask.java @@ -97,7 +97,8 @@ else if (MediaType.TEXT_PLAIN.equals(type)) ctx.getLogger().info("Single specimen file detected, moving to a temp folder for processing."); String tempDirName = DateUtil.formatDateTime(new Date(), "yyMMddHHmmssSSS"); _tempDir = inputFile.getParent().resolve(tempDirName); - Files.copy(inputFile, _tempDir); + Files.createDirectory(_tempDir); + Files.copy(inputFile, _tempDir.resolve(inputFile.getFileName())); return new FileSystemFile(_tempDir); } From 7fd80969fbc8b4ba43136c211f51721acb4f4a50 Mon Sep 17 00:00:00 2001 From: Ian Sigmon Date: Tue, 15 Mar 2022 12:43:56 -0700 Subject: [PATCH 362/494] Close open stream that was causing IOExceptions Original PR: https://github.com/LabKey/platform/pull/3131 Close workbooks and other file handles Co-authored-by: labkey-tchad --- .../pipeline/SampleMindedTransformTask.java | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/specimen/src/org/labkey/specimen/pipeline/SampleMindedTransformTask.java b/specimen/src/org/labkey/specimen/pipeline/SampleMindedTransformTask.java index 5782cb46..b125c344 100644 --- a/specimen/src/org/labkey/specimen/pipeline/SampleMindedTransformTask.java +++ b/specimen/src/org/labkey/specimen/pipeline/SampleMindedTransformTask.java @@ -498,13 +498,15 @@ static void importXls(@NotNull TableInfo target, @NotNull File source, PipelineJ DataLoaderFactory df = DataLoader.get().findFactory(source, null); if (null == df) return; - DataLoader dl = df.createLoader(source, true, study.getContainer()); - DataIteratorBuilder sampleminded = wrapSampleMindedTransform(job.getUser(), dl, context, study, target); - Map empty = new HashMap<>(); + try (DataLoader dl = df.createLoader(source, true, study.getContainer())) + { + DataIteratorBuilder sampleminded = wrapSampleMindedTransform(job.getUser(), dl, context, study, target); + Map empty = new HashMap<>(); - // would be nice to have deleteAll() in QueryUpdateService - new SqlExecutor(scope).execute("DELETE FROM rho." + target.getName() + " WHERE container=?", study.getContainer()); - int count = target.getUpdateService().importRows(job.getUser(), study.getContainer(), sampleminded, context.getErrors(), null, empty); + // would be nice to have deleteAll() in QueryUpdateService + new SqlExecutor(scope).execute("DELETE FROM rho." + target.getName() + " WHERE container=?", study.getContainer()); + int count = target.getUpdateService().importRows(job.getUser(), study.getContainer(), sampleminded, context.getErrors(), null, empty); + } if (!context.getErrors().hasErrors()) { transaction.commit(); From 1a697b6f7e0e6a99bc042c22a95887cf36d50c5c Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Wed, 16 Mar 2022 14:41:49 -0700 Subject: [PATCH 363/494] Miscellaneous cleanups Original PR: https://github.com/LabKey/platform/pull/3140 --- .../org/labkey/specimen/SpecimenModule.java | 6 ---- .../importer/AbstractSpecimenTask.java | 2 +- .../specimen/pipeline/SpecimenBatch.java | 1 - .../specimen/pipeline/SpecimenJobSupport.java | 34 +++++++++++++++++++ .../pipeline/SpecimenReloadJobSupport.java | 1 - 5 files changed, 35 insertions(+), 9 deletions(-) create mode 100644 specimen/src/org/labkey/specimen/pipeline/SpecimenJobSupport.java diff --git a/specimen/src/org/labkey/specimen/SpecimenModule.java b/specimen/src/org/labkey/specimen/SpecimenModule.java index 773eee84..cd703bfc 100644 --- a/specimen/src/org/labkey/specimen/SpecimenModule.java +++ b/specimen/src/org/labkey/specimen/SpecimenModule.java @@ -78,8 +78,6 @@ public class SpecimenModule extends SpringModule { public static final String NAME = "Specimen"; - // TODO: Delete this and all associated code - public static final String CREATE_SPECIMEN_STUDY = "CreateSpecimenStudy"; @Override public String getName() @@ -227,10 +225,6 @@ protected void startupAfterSpringConfig(ModuleContext moduleContext) new SpecimenSchemaImporter(), new SpecimenSettingsImporter() )); - - // TODO: Remove this... we're no longer respecting this flag - AdminConsole.addExperimentalFeatureFlag(CREATE_SPECIMEN_STUDY, "Create Specimen Study", - "Adds a button to the specimen request details page that creates a new child study containing the selected specimens, associated participants, and selected datasets.", false); } @Override diff --git a/specimen/src/org/labkey/specimen/importer/AbstractSpecimenTask.java b/specimen/src/org/labkey/specimen/importer/AbstractSpecimenTask.java index 104ca92c..4c1b780c 100644 --- a/specimen/src/org/labkey/specimen/importer/AbstractSpecimenTask.java +++ b/specimen/src/org/labkey/specimen/importer/AbstractSpecimenTask.java @@ -26,7 +26,6 @@ import org.labkey.api.pipeline.RecordedActionSet; import org.labkey.api.pipeline.TaskFactory; import org.labkey.api.specimen.importer.SpecimenImporter; -import org.labkey.api.specimen.pipeline.SpecimenJobSupport; import org.labkey.api.study.SpecimenService; import org.labkey.api.study.SpecimenTransform; import org.labkey.api.study.Study; @@ -39,6 +38,7 @@ import org.labkey.api.writer.FileSystemFile; import org.labkey.api.writer.VirtualFile; import org.labkey.api.writer.ZipUtil; +import org.labkey.specimen.pipeline.SpecimenJobSupport; import org.labkey.specimen.writer.SpecimenArchiveDataTypes; import java.io.File; diff --git a/specimen/src/org/labkey/specimen/pipeline/SpecimenBatch.java b/specimen/src/org/labkey/specimen/pipeline/SpecimenBatch.java index acf9b102..e072c092 100644 --- a/specimen/src/org/labkey/specimen/pipeline/SpecimenBatch.java +++ b/specimen/src/org/labkey/specimen/pipeline/SpecimenBatch.java @@ -23,7 +23,6 @@ import org.labkey.api.pipeline.TaskPipeline; import org.labkey.api.portal.ProjectUrls; import org.labkey.api.specimen.SpecimensPage; -import org.labkey.api.specimen.pipeline.SpecimenJobSupport; import org.labkey.api.study.importer.SimpleStudyImportContext; import org.labkey.api.study.pipeline.StudyBatch; import org.labkey.api.util.FileUtil; diff --git a/specimen/src/org/labkey/specimen/pipeline/SpecimenJobSupport.java b/specimen/src/org/labkey/specimen/pipeline/SpecimenJobSupport.java new file mode 100644 index 00000000..fb9b983d --- /dev/null +++ b/specimen/src/org/labkey/specimen/pipeline/SpecimenJobSupport.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2009-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.specimen.pipeline; + +import org.labkey.api.admin.ImportException; +import org.labkey.api.study.importer.SimpleStudyImportContext; + +import java.nio.file.Path; + +/* +* User: adam +* Date: Sep 1, 2009 +* Time: 3:02:30 PM +*/ +public interface SpecimenJobSupport +{ + Path getSpecimenArchivePath() throws ImportException; + boolean isMerge(); + SimpleStudyImportContext getImportContext(); +} diff --git a/specimen/src/org/labkey/specimen/pipeline/SpecimenReloadJobSupport.java b/specimen/src/org/labkey/specimen/pipeline/SpecimenReloadJobSupport.java index 64c912fa..42f6a61a 100644 --- a/specimen/src/org/labkey/specimen/pipeline/SpecimenReloadJobSupport.java +++ b/specimen/src/org/labkey/specimen/pipeline/SpecimenReloadJobSupport.java @@ -15,7 +15,6 @@ */ package org.labkey.specimen.pipeline; -import org.labkey.api.specimen.pipeline.SpecimenJobSupport; import org.labkey.api.study.SpecimenTransform; import java.io.File; From e707b53cb70525779266ecce3966b0e3fd28dca9 Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Fri, 18 Mar 2022 15:24:54 -0700 Subject: [PATCH 364/494] Modernize and optimize missing value indicator cache Original PR: https://github.com/LabKey/platform/pull/3155 --- specimen/src/org/labkey/specimen/SpecimenRequestManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specimen/src/org/labkey/specimen/SpecimenRequestManager.java b/specimen/src/org/labkey/specimen/SpecimenRequestManager.java index 973c8b18..63d82888 100644 --- a/specimen/src/org/labkey/specimen/SpecimenRequestManager.java +++ b/specimen/src/org/labkey/specimen/SpecimenRequestManager.java @@ -941,7 +941,7 @@ public Map> getGroupedValuesForColumn(Container cont else { _groupedValuesCache = new DatabaseCache<>( - SpecimenSchema.get().getScope(), 10, 8 * CacheManager.HOUR, "Grouped Values Cache"); + SpecimenSchema.get().getScope(), 10, 8 * CacheManager.HOUR, "Specimen grouped values"); } try From ec7084e44c992e3ece249dbd1bb687fa66fbad2e Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Thu, 7 Apr 2022 14:47:00 -0700 Subject: [PATCH 365/494] Refactor import/export context and writer hierarchies Original PR: https://github.com/LabKey/platform/pull/3217 --- .../labkey/specimen/writer/AbstractSpecimenWriter.java | 4 ++-- .../labkey/specimen/writer/LocationSpecimenWriter.java | 5 ++--- .../labkey/specimen/writer/StandardSpecimenWriter.java | 9 ++++----- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/specimen/src/org/labkey/specimen/writer/AbstractSpecimenWriter.java b/specimen/src/org/labkey/specimen/writer/AbstractSpecimenWriter.java index 0eebdecb..c8cc3c11 100644 --- a/specimen/src/org/labkey/specimen/writer/AbstractSpecimenWriter.java +++ b/specimen/src/org/labkey/specimen/writer/AbstractSpecimenWriter.java @@ -15,7 +15,7 @@ */ package org.labkey.specimen.writer; -import org.labkey.api.study.writer.AbstractContext; +import org.labkey.api.study.writer.AbstractStudyContext; import org.labkey.api.study.writer.SimpleStudyWriter; import org.labkey.study.xml.StudyDocument; @@ -33,7 +33,7 @@ public boolean includeWithTemplate() return true; } - protected StudyDocument.Study.Specimens ensureSpecimensElement(AbstractContext ctx) throws Exception + protected StudyDocument.Study.Specimens ensureSpecimensElement(AbstractStudyContext ctx) throws Exception { StudyDocument.Study studyXml = ctx.getXml(); StudyDocument.Study.Specimens specimensXml; diff --git a/specimen/src/org/labkey/specimen/writer/LocationSpecimenWriter.java b/specimen/src/org/labkey/specimen/writer/LocationSpecimenWriter.java index 080c1f96..babc4e76 100644 --- a/specimen/src/org/labkey/specimen/writer/LocationSpecimenWriter.java +++ b/specimen/src/org/labkey/specimen/writer/LocationSpecimenWriter.java @@ -15,12 +15,11 @@ */ package org.labkey.specimen.writer; -import org.labkey.api.admin.ImportContext; import org.labkey.api.data.SQLFragment; import org.labkey.api.data.TableInfo; import org.labkey.api.data.dialect.SqlDialect; import org.labkey.api.specimen.importer.ImportableColumn; -import org.labkey.study.xml.StudyDocument; +import org.labkey.api.study.writer.SimpleStudyExportContext; import java.util.Collection; @@ -31,7 +30,7 @@ public class LocationSpecimenWriter extends StandardSpecimenWriter { @Override - protected SQLFragment generateSql(ImportContext ctx, TableInfo tinfo, Collection columns) + protected SQLFragment generateSql(SimpleStudyExportContext ctx, TableInfo tinfo, Collection columns) { SqlDialect d = tinfo.getSqlDialect(); diff --git a/specimen/src/org/labkey/specimen/writer/StandardSpecimenWriter.java b/specimen/src/org/labkey/specimen/writer/StandardSpecimenWriter.java index 2b212046..840ba294 100644 --- a/specimen/src/org/labkey/specimen/writer/StandardSpecimenWriter.java +++ b/specimen/src/org/labkey/specimen/writer/StandardSpecimenWriter.java @@ -15,7 +15,6 @@ */ package org.labkey.specimen.writer; -import org.labkey.api.admin.ImportContext; import org.labkey.api.data.ColumnInfo; import org.labkey.api.data.DataColumn; import org.labkey.api.data.DisplayColumn; @@ -27,9 +26,9 @@ import org.labkey.api.data.TableInfo; import org.labkey.api.specimen.SpecimenSchema; import org.labkey.api.specimen.importer.ImportableColumn; +import org.labkey.api.study.writer.SimpleStudyExportContext; import org.labkey.api.writer.VirtualFile; import org.labkey.api.writer.Writer; -import org.labkey.study.xml.StudyDocument; import java.io.PrintWriter; import java.util.ArrayList; @@ -41,7 +40,7 @@ * Date: May 7, 2009 * Time: 9:51:36 AM */ -class StandardSpecimenWriter implements Writer> +class StandardSpecimenWriter implements Writer { @Override public String getDataType() @@ -50,7 +49,7 @@ public String getDataType() } @Override - public void write(QueryInfo queryInfo, ImportContext ctx, VirtualFile vf) throws Exception + public void write(QueryInfo queryInfo, SimpleStudyExportContext ctx, VirtualFile vf) throws Exception { TableInfo tinfo = queryInfo.getTableInfo(); Collection columns = queryInfo.getColumns(); @@ -84,7 +83,7 @@ public void write(QueryInfo queryInfo, ImportContext ctx, V } } - protected SQLFragment generateSql(ImportContext ctx, TableInfo tinfo, Collection columns) + protected SQLFragment generateSql(SimpleStudyExportContext ctx, TableInfo tinfo, Collection columns) { SQLFragment sql = new SQLFragment().append("SELECT "); String comma = ""; From 13ccdbffcaea696c55219b537126a130d513dc37 Mon Sep 17 00:00:00 2001 From: labkey-matthewb Date: Mon, 18 Apr 2022 13:43:33 -0700 Subject: [PATCH 366/494] Remove inline script event handlers Original PR: https://github.com/LabKey/platform/pull/3192 PageConfig.getScriptNonce() --- .../specimen/query/SpecimenRequestQueryView.java | 14 +++++++++++--- .../org/labkey/specimen/view/autoReportList.jsp | 2 +- .../specimen/view/configRequestabilityRules.jsp | 2 +- .../org/labkey/specimen/view/manageActorOrder.jsp | 2 +- .../org/labkey/specimen/view/manageDefaultReqs.jsp | 2 +- .../labkey/specimen/view/manageLocationTypes.jsp | 2 +- .../labkey/specimen/view/manageNotifications.jsp | 2 +- .../src/org/labkey/specimen/view/manageRequest.jsp | 2 +- .../labkey/specimen/view/manageRequestInputs.jsp | 2 +- .../labkey/specimen/view/manageSpecimenWebPart.jsp | 2 +- .../org/labkey/specimen/view/manageStatusOrder.jsp | 2 +- .../org/labkey/specimen/view/manageStatuses.jsp | 2 +- specimen/src/org/labkey/specimen/view/pivot.jsp | 2 +- .../org/labkey/specimen/view/requestSpecimens.jsp | 2 +- specimen/src/org/labkey/specimen/view/search.jsp | 2 +- .../org/labkey/specimen/view/specimenHeader.jsp | 2 +- specimen/src/org/labkey/specimen/view/webPart.jsp | 12 ++++-------- 17 files changed, 30 insertions(+), 26 deletions(-) diff --git a/specimen/src/org/labkey/specimen/query/SpecimenRequestQueryView.java b/specimen/src/org/labkey/specimen/query/SpecimenRequestQueryView.java index d9390ef1..769160c8 100644 --- a/specimen/src/org/labkey/specimen/query/SpecimenRequestQueryView.java +++ b/specimen/src/org/labkey/specimen/query/SpecimenRequestQueryView.java @@ -129,19 +129,25 @@ public void renderGridCellContents(RenderContext ctx, Writer out) throws IOExcep ActionURL submitUrl = new ActionURL(SubmitRequestAction.class, ctx.getContainer()).addParameter("id", "${requestId}");; ActionURL cancelUrl = new ActionURL(DeleteRequestAction.class, ctx.getContainer()).addParameter("id", "${requestId}");; - content.append(PageFlowUtil.button("Submit").href(submitUrl).usePost(StudyUtils.SUBMISSION_WARNING)); - content.append(PageFlowUtil.button("Cancel").href(cancelUrl).usePost(StudyUtils.CANCELLATION_WARNING)); + content.append(PageFlowUtil.button("Submit").href(submitUrl).usePost(StudyUtils.SUBMISSION_WARNING).inlineScript()); + content.append(PageFlowUtil.button("Cancel").href(cancelUrl).usePost(StudyUtils.CANCELLATION_WARNING).inlineScript()); } } ActionURL detailsUrl = new ActionURL(ManageRequestAction.class, ctx.getContainer()).addParameter("id", "${requestId}"); - content.append(PageFlowUtil.button("Details").href(detailsUrl)); + content.append(PageFlowUtil.button("Details").href(detailsUrl).inlineScript()); } content.append(""); setDisplayHtml(content.toString()); super.renderGridCellContents(ctx, out); } + + @Override + public String getName() + { + return "RequestOptions"; + } } protected SpecimenRequestQueryView(UserSchema schema, QuerySettings settings, SimpleFilter filter, Sort sort, NavTree... extraLinks) @@ -222,4 +228,6 @@ public void setShowCustomizeLink(boolean showCustomizeLink) { getSettings().setAllowCustomizeView(showCustomizeLink); } + + } diff --git a/specimen/src/org/labkey/specimen/view/autoReportList.jsp b/specimen/src/org/labkey/specimen/view/autoReportList.jsp index b347fab0..c36a6eaa 100644 --- a/specimen/src/org/labkey/specimen/view/autoReportList.jsp +++ b/specimen/src/org/labkey/specimen/view/autoReportList.jsp @@ -65,7 +65,7 @@ This folder does not contain a study. else { %> - @@ -184,7 +204,7 @@ SpecimenRequestInput input = inputs[inputIndex]; %> - + From 2689bd29ad94360616722843bd51cc3076f6ced4 Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Thu, 16 Nov 2023 11:34:19 -0800 Subject: [PATCH 401/494] Migrate Ext3 -> Ext4 to eliminate inline handlers Original PR: https://github.com/LabKey/platform/pull/4959 --- .../view/configRequestabilityRules.jsp | 274 +++++++++--------- 1 file changed, 132 insertions(+), 142 deletions(-) diff --git a/specimen/src/org/labkey/specimen/view/configRequestabilityRules.jsp b/specimen/src/org/labkey/specimen/view/configRequestabilityRules.jsp index 196946f6..c71c6334 100644 --- a/specimen/src/org/labkey/specimen/view/configRequestabilityRules.jsp +++ b/specimen/src/org/labkey/specimen/view/configRequestabilityRules.jsp @@ -26,7 +26,7 @@ @Override public void addClientDependencies(ClientDependencies dependencies) { - dependencies.add("Ext3"); + dependencies.add("Ext4"); } %> <% @@ -34,24 +34,23 @@ Container c = getContainer(); %> @@ -664,7 +654,7 @@ %> - + <% } From 246146aa51717c7d9de3aed00f290eb3ed6a9134 Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Thu, 16 Nov 2023 11:34:28 -0800 Subject: [PATCH 402/494] Use Ext4 locators Original PR: https://github.com/LabKey/testAutomation/pull/1728 --- src/org/labkey/test/tests/AliquotTest.java | 32 +++---------------- .../labkey/test/tests/SpecimenBaseTest.java | 26 +++++++++++---- src/org/labkey/test/tests/SpecimenTest.java | 7 ++++ 3 files changed, 31 insertions(+), 34 deletions(-) diff --git a/src/org/labkey/test/tests/AliquotTest.java b/src/org/labkey/test/tests/AliquotTest.java index 6a801c08..0028f416 100644 --- a/src/org/labkey/test/tests/AliquotTest.java +++ b/src/org/labkey/test/tests/AliquotTest.java @@ -93,37 +93,15 @@ protected void doVerifySteps() @LogMethod protected void setupRequestabilityRules() { - // Create custom query to test requestability rules. - goToSchemaBrowser(); - createNewQuery("study", SPECIMEN_DETAIL); - setFormElement(Locator.name("ff_newQueryName"), REQUESTABILITY_QUERY); - clickAndWait(Locator.lkButton("Create and Edit Source")); - setCodeEditorValue("queryText", - "SELECT \n" + - SPECIMEN_DETAIL + ".GlobalUniqueId AS GlobalUniqueId\n" + - "FROM " + SPECIMEN_DETAIL + "\n" + - "WHERE " + SPECIMEN_DETAIL + ".GlobalUniqueId='" + UNREQUESTABLE_SAMPLE + "'"); - clickButton("Save", 0); - waitForText(WAIT_FOR_JAVASCRIPT, "Saved"); - - clickFolder(getFolderName()); - waitAndClick(Locator.linkWithText("Manage Study")); - waitAndClick(Locator.linkWithText("Manage Requestability Rules")); - waitForElement(Locator.xpath("//div[contains(@class, 'x-grid3-row')]//div[text()='Locked In Request Check']")); - - clickButton("Add Rule", 0); - click(Locator.menuItem("Custom Query")); - _extHelper.selectComboBoxItem(Locator.xpath("//div[@id='x-form-el-userQuery_schema']"), "study" ); - _extHelper.selectComboBoxItem(Locator.xpath("//div[@id='x-form-el-userQuery_query']"), REQUESTABILITY_QUERY ); - _extHelper.selectComboBoxItem(Locator.xpath("//div[@id='x-form-el-userQuery_action']"), "Unavailable" ); - clickButton("Submit", 0); + super.setupRequestabilityRules(); clickButton("Add Rule", 0); click(Locator.menuItem("Locked While Processing Check")); - // Remove Locked In Request - waitForElement(Locator.xpath("//div[contains(@class, 'x-grid3-row')]//div[text()='Locked In Request Check']")); - click(Locator.xpath("//div[contains(@class, 'x-grid3-row')]//div[text()='Locked In Request Check']")); + // Remove "Locked In Request" rule + Locator lockedInRequestRow = Locator.xpath("//tr[contains(@class, 'x4-grid-row')]//div[text()='Locked In Request Check']"); + waitForElement(lockedInRequestRow); + click(lockedInRequestRow); clickButton("Remove Rule", 0); clickButton("Save"); diff --git a/src/org/labkey/test/tests/SpecimenBaseTest.java b/src/org/labkey/test/tests/SpecimenBaseTest.java index 8e991e45..25c52e34 100644 --- a/src/org/labkey/test/tests/SpecimenBaseTest.java +++ b/src/org/labkey/test/tests/SpecimenBaseTest.java @@ -16,10 +16,12 @@ package org.labkey.test.tests; +import org.junit.Assert; import org.labkey.test.Locator; import org.labkey.test.TestTimeoutException; import org.labkey.test.components.html.RadioButton; import org.labkey.test.util.LogMethod; +import org.labkey.test.util.ext4cmp.Ext4GridRef; import static org.labkey.test.components.html.RadioButton.RadioButton; @@ -41,6 +43,7 @@ protected void doCleanup(boolean afterTest) throws TestTimeoutException super.doCleanup(afterTest); } + // Callers must click the Save button @LogMethod protected void setupRequestabilityRules() { @@ -60,19 +63,28 @@ protected void setupRequestabilityRules() clickFolder(getFolderName()); waitAndClick(Locator.linkWithText("Manage Study")); waitAndClick(Locator.linkWithText("Manage Requestability Rules")); + + // Wait for grid to render + waitForElement(Locator.xpath("//table[contains(@class, 'x4-grid-table')]")); + Ext4GridRef gridRef = _ext4Helper.queryOne("panel[title='Active Rules']", Ext4GridRef.class); + + // Expect three rows + int rowCount = gridRef.getRowCount(); + log("How many rows? " + rowCount); + Assert.assertEquals(3, rowCount); + // Verify that LOCKED_IN_REQUEST is the last rule - waitForElement(Locator.xpath("//div[contains(@class, 'x-grid3-row-last')]//div[text()='Locked In Request Check']")); - click(Locator.xpath("//div[contains(@class, 'x-grid3-row-last')]//div[text()='Locked In Request Check']")); + log(gridRef.getFieldValue(rowCount, "name").toString()); + Assert.assertEquals("Last requestability rule", "Locked In Request Check", gridRef.getFieldValue(rowCount, "name")); - click(Locator.xpath("//div[contains(@class, 'x-grid3-col-numberer') and text()='2']")); + click(Locator.xpath("//div[contains(@class, 'x4-grid-cell-inner-row-numberer') and text()='2']")); clickButton("Add Rule", 0); click(Locator.menuItem("Custom Query")); - _extHelper.selectComboBoxItem(Locator.xpath("//div[@id='x-form-el-userQuery_schema']"), "study" ); - _extHelper.selectComboBoxItem(Locator.xpath("//div[@id='x-form-el-userQuery_query']"), REQUESTABILITY_QUERY ); - _extHelper.selectComboBoxItem(Locator.xpath("//div[@id='x-form-el-userQuery_action']"), "Unavailable" ); + _ext4Helper.selectComboBoxItem(Locator.id("userQuery_schema"), "study" ); + _ext4Helper.selectComboBoxItem(Locator.id("userQuery_query"), REQUESTABILITY_QUERY ); + _ext4Helper.selectComboBoxItem(Locator.id("userQuery_action"), "Unavailable" ); clickButton("Submit",0); - clickButton("Save"); } @LogMethod diff --git a/src/org/labkey/test/tests/SpecimenTest.java b/src/org/labkey/test/tests/SpecimenTest.java index 36021c26..8443da0f 100644 --- a/src/org/labkey/test/tests/SpecimenTest.java +++ b/src/org/labkey/test/tests/SpecimenTest.java @@ -120,6 +120,13 @@ protected void doCreateSteps() uploadSpecimensFromFile(); } + @Override + protected void setupRequestabilityRules() + { + super.setupRequestabilityRules(); + clickButton("Save"); + } + @Override @LogMethod protected void doVerifySteps() throws IOException From b8856e4058e8fb615a48e1e38df05912189389aa Mon Sep 17 00:00:00 2001 From: labkey-matthewb Date: Thu, 7 Dec 2023 16:30:40 -0800 Subject: [PATCH 403/494] replace new HtmlView(String) with HtmlView.of()/unsafe()/err() Original PR: https://github.com/LabKey/platform/pull/5025 --- .../specimen/actions/SpecimenController.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/specimen/src/org/labkey/specimen/actions/SpecimenController.java b/specimen/src/org/labkey/specimen/actions/SpecimenController.java index cd384f7a..3a0b46ee 100644 --- a/specimen/src/org/labkey/specimen/actions/SpecimenController.java +++ b/specimen/src/org/labkey/specimen/actions/SpecimenController.java @@ -770,7 +770,7 @@ public class OverviewAction extends SimpleViewAction public ModelAndView getView(Object form, BindException errors) { if (null == StudyService.get().getStudy(getContainer())) - return new HtmlView("This folder does not contain a study."); + return HtmlView.of("This folder does not contain a study."); SpecimenSearchWebPart specimenSearch = new SpecimenSearchWebPart(true); SpecimenWebPart specimenSummary = new SpecimenWebPart(true, StudyService.get().getStudy(getContainer())); return new VBox(specimenSummary, specimenSearch); @@ -1179,7 +1179,7 @@ public ModelAndView getView(SpecimenEventForm form, BindException errors) throw new RedirectException(url); } } - return new HtmlView("Unable to resolve the Specimen ID and target Study"); + return HtmlView.err("Unable to resolve the Specimen ID and target Study"); } @Override @@ -2619,7 +2619,7 @@ public ModelAndView getView(CreateSpecimenRequestForm form, BindException errors } else { - vbox.addView(new HtmlView("An extended specimen request view has not been provided for this folder.")); + vbox.addView(HtmlView.of("An extended specimen request view has not been provided for this folder.")); } return vbox; } @@ -3471,7 +3471,7 @@ public ModelAndView getView(IdForm form, BindException errors) throws Exception if (!SpecimenRequestManager.get().hasEditRequestPermissions(getUser(), request) || SpecimenRequestManager.get().isInFinalState(request)) { - return new HtmlView("
You do not have permissions to modify this request.
"); + return HtmlView.err("You do not have permissions to modify this request."); } return getDefaultImportView(form, errors); @@ -4099,7 +4099,7 @@ public ModelAndView getView(UpdateParticipantCommentsForm specimenCommentsForm, } } if (selectedVials == null || selectedVials.size() == 0) - return new HtmlView("No vials selected. " + PageFlowUtil.link("back").href("javascript:back()")); + return HtmlView.unsafe("No vials selected. " + PageFlowUtil.link("back").onClick("back()")); } return new JspView<>("/org/labkey/specimen/view/updateComments.jsp", @@ -5487,7 +5487,7 @@ public ModelAndView getView(ManageCommentsForm form, boolean reshow, BindExcepti StudyInternalService svc = StudyInternalService.get(); if (!svc.hasEditableDatasets(study)) - return new HtmlView("Comments can only be configured for studies with editable datasets."); + return HtmlView.of("Comments can only be configured for studies with editable datasets."); if (!form.isReshow()) { @@ -5641,7 +5641,7 @@ public ModelAndView getView(final ParticipantCommentForm form, BindException err url.addParameter(ParticipantCommentForm.params.vialCommentsToClear, rowId); return HttpView.redirect(url); } - return new HtmlView("Dataset could not be found"); + return HtmlView.of("Dataset could not be found"); } @Override From 8a1338555bc6562d010049570e7080cbb02a2595 Mon Sep 17 00:00:00 2001 From: labkey-matthewb Date: Tue, 12 Dec 2023 16:58:24 -0800 Subject: [PATCH 404/494] HtmlStringBuilder Original PR: https://github.com/LabKey/platform/pull/5029 * convert from StringBuilder -> HtmlStringBuilder checkpoint I * convert from StringBuilder -> HtmlStringBuilder checkpoint II * convert from StringBuilder -> HtmlStringBuilder class Overview * CR fixes * HtmlStringBuilder.insert() --- .../src/org/labkey/specimen/actions/SpecimenController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specimen/src/org/labkey/specimen/actions/SpecimenController.java b/specimen/src/org/labkey/specimen/actions/SpecimenController.java index 3a0b46ee..d0229f95 100644 --- a/specimen/src/org/labkey/specimen/actions/SpecimenController.java +++ b/specimen/src/org/labkey/specimen/actions/SpecimenController.java @@ -2614,7 +2614,7 @@ public ModelAndView getView(CreateSpecimenRequestForm form, BindException errors ExtendedSpecimenRequestView view = SpecimenManager.get().getExtendedSpecimenRequestView(getViewContext()); if (view != null && view.isActive()) { - HtmlView requestView = new HtmlView(view.getBody()); + HtmlView requestView = HtmlView.unsafe(view.getBody()); vbox.addView(requestView); } else From 4bd6f1166f310fd030eecb62f55e39644e4a728d Mon Sep 17 00:00:00 2001 From: labkey-matthewb Date: Mon, 18 Dec 2023 09:06:42 -0800 Subject: [PATCH 405/494] remove usages of HtmlView(String,String) Original PR: https://github.com/LabKey/platform/pull/5054 --- .../labkey/specimen/view/SpecimenSearchWebPartFactory.java | 5 +++-- .../src/org/labkey/specimen/view/SpecimenWebPartFactory.java | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/specimen/src/org/labkey/specimen/view/SpecimenSearchWebPartFactory.java b/specimen/src/org/labkey/specimen/view/SpecimenSearchWebPartFactory.java index 5664e8a3..b820d411 100644 --- a/specimen/src/org/labkey/specimen/view/SpecimenSearchWebPartFactory.java +++ b/specimen/src/org/labkey/specimen/view/SpecimenSearchWebPartFactory.java @@ -3,6 +3,7 @@ import org.jetbrains.annotations.NotNull; import org.labkey.api.security.permissions.ReadPermission; import org.labkey.api.study.StudyService; +import org.labkey.api.util.HtmlString; import org.labkey.api.view.DefaultWebPartFactory; import org.labkey.api.view.HtmlView; import org.labkey.api.view.Portal; @@ -21,10 +22,10 @@ public SpecimenSearchWebPartFactory(String position) public WebPartView getWebPartView(@NotNull ViewContext portalCtx, @NotNull Portal.WebPart webPart) { if (!portalCtx.hasPermission(ReadPermission.class)) - return new HtmlView("Specimens", portalCtx.getUser().isGuest() ? "Please log in to see this data." : "You do not have permission to see this data"); + return new HtmlView("Specimens", HtmlString.of(portalCtx.getUser().isGuest() ? "Please log in to see this data." : "You do not have permission to see this data")); if (null == StudyService.get().getStudy(portalCtx.getContainer())) - return new HtmlView("Specimens", "This folder does not contain a study."); + return new HtmlView("Specimens", HtmlString.of("This folder does not contain a study.")); return new SpecimenSearchWebPart(true); } } diff --git a/specimen/src/org/labkey/specimen/view/SpecimenWebPartFactory.java b/specimen/src/org/labkey/specimen/view/SpecimenWebPartFactory.java index 3630d1ff..e9c2dcfc 100644 --- a/specimen/src/org/labkey/specimen/view/SpecimenWebPartFactory.java +++ b/specimen/src/org/labkey/specimen/view/SpecimenWebPartFactory.java @@ -4,6 +4,7 @@ import org.labkey.api.security.permissions.ReadPermission; import org.labkey.api.study.Study; import org.labkey.api.study.StudyService; +import org.labkey.api.util.HtmlString; import org.labkey.api.view.DefaultWebPartFactory; import org.labkey.api.view.HtmlView; import org.labkey.api.view.HttpView; @@ -24,11 +25,11 @@ public SpecimenWebPartFactory() public WebPartView getWebPartView(@NotNull ViewContext portalCtx, @NotNull Portal.WebPart webPart) { if (!portalCtx.hasPermission(ReadPermission.class)) - return new HtmlView("Specimens", portalCtx.getUser().isGuest() ? "Please log in to see this data." : "You do not have permission to see this data"); + return new HtmlView("Specimens", HtmlString.of(portalCtx.getUser().isGuest() ? "Please log in to see this data." : "You do not have permission to see this data")); Study study = StudyService.get().getStudy(portalCtx.getContainer()); if (null == study) - return new HtmlView("Specimens", "This folder does not contain a study."); + return new HtmlView("Specimens", HtmlString.of("This folder does not contain a study.")); return new SpecimenWebPart(webPart.getLocation().equals(HttpView.BODY), study); } } From 5747b06fb03d330158b9644248818b0fe620b8c4 Mon Sep 17 00:00:00 2001 From: Karl Lum Date: Fri, 29 Dec 2023 13:13:32 -0800 Subject: [PATCH 406/494] Allow studies to prevent new timepoint creation on data import. Original PR: https://github.com/LabKey/platform/pull/5075 --- specimen/src/org/labkey/specimen/SpecimenModule.java | 3 ++- .../specimen/actions/ShowUploadSpecimensAction.java | 3 ++- .../specimen/importer/AbstractSpecimenTask.java | 11 ++++++++--- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/specimen/src/org/labkey/specimen/SpecimenModule.java b/specimen/src/org/labkey/specimen/SpecimenModule.java index 7b27157e..b01650ee 100644 --- a/specimen/src/org/labkey/specimen/SpecimenModule.java +++ b/specimen/src/org/labkey/specimen/SpecimenModule.java @@ -38,6 +38,7 @@ import org.labkey.api.query.FieldKey; import org.labkey.api.query.QueryParam; import org.labkey.api.query.QueryView; +import org.labkey.api.query.ValidationException; import org.labkey.api.security.User; import org.labkey.api.security.roles.RoleManager; import org.labkey.api.specimen.SpecimenMigrationService; @@ -189,7 +190,7 @@ public ActionURL getUpdateSpecimenQueryRowURL(Container c, String schemaName, Ta } @Override - public void importSpecimenArchive(@Nullable Path inputFile, PipelineJob job, SimpleStudyImportContext ctx, boolean merge, boolean syncParticipantVisit) throws PipelineJobException + public void importSpecimenArchive(@Nullable Path inputFile, PipelineJob job, SimpleStudyImportContext ctx, boolean merge, boolean syncParticipantVisit) throws PipelineJobException, ValidationException { AbstractSpecimenTask.doImport(inputFile, job, ctx, merge, syncParticipantVisit); } diff --git a/specimen/src/org/labkey/specimen/actions/ShowUploadSpecimensAction.java b/specimen/src/org/labkey/specimen/actions/ShowUploadSpecimensAction.java index 4671ac5e..ffe32443 100644 --- a/specimen/src/org/labkey/specimen/actions/ShowUploadSpecimensAction.java +++ b/specimen/src/org/labkey/specimen/actions/ShowUploadSpecimensAction.java @@ -25,6 +25,7 @@ import org.labkey.api.data.RuntimeSQLException; import org.labkey.api.pipeline.PipelineUrls; import org.labkey.api.portal.ProjectUrls; +import org.labkey.api.query.ValidationException; import org.labkey.api.reader.ColumnDescriptor; import org.labkey.api.reader.TabLoader; import org.labkey.api.security.RequiresPermission; @@ -262,7 +263,7 @@ else if (study.getTimepointType() == TimepointType.VISIT && null == row.get(Simp if (!errors.hasErrors()) importer.process(specimenRows, form.isMerge()); } - catch (IllegalStateException e) + catch (IllegalStateException | ValidationException e) { errors.reject(SpringActionController.ERROR_MSG, e.getMessage()); } diff --git a/specimen/src/org/labkey/specimen/importer/AbstractSpecimenTask.java b/specimen/src/org/labkey/specimen/importer/AbstractSpecimenTask.java index 4c1b780c..d48db7d1 100644 --- a/specimen/src/org/labkey/specimen/importer/AbstractSpecimenTask.java +++ b/specimen/src/org/labkey/specimen/importer/AbstractSpecimenTask.java @@ -25,6 +25,7 @@ import org.labkey.api.pipeline.PipelineJobException; import org.labkey.api.pipeline.RecordedActionSet; import org.labkey.api.pipeline.TaskFactory; +import org.labkey.api.query.ValidationException; import org.labkey.api.specimen.importer.SpecimenImporter; import org.labkey.api.study.SpecimenService; import org.labkey.api.study.SpecimenTransform; @@ -98,7 +99,7 @@ protected SimpleStudyImportContext getImportContext(PipelineJob job) } public static void doImport(@Nullable Path inputFile, PipelineJob job, SimpleStudyImportContext ctx, boolean merge, - boolean syncParticipantVisit) throws PipelineJobException + boolean syncParticipantVisit) throws PipelineJobException, ValidationException { doImport(inputFile, job, ctx, merge, syncParticipantVisit, new DefaultImportHelper()); } @@ -115,7 +116,7 @@ private static void doPostTransform(SpecimenTransform transformer, Path inputFil } public static void doImport(@Nullable Path inputFile, PipelineJob job, SimpleStudyImportContext ctx, boolean merge, - boolean syncParticipantVisit, ImportHelper importHelper) throws PipelineJobException + boolean syncParticipantVisit, ImportHelper importHelper) throws PipelineJobException, ValidationException { // do nothing if we've specified data types and specimen is not one of them if (!ctx.isDataTypeSelected(SpecimenArchiveDataTypes.SPECIMENS)) @@ -172,7 +173,11 @@ public static void doImport(@Nullable Path inputFile, PipelineJob job, SimpleStu // Since changing specimens in this study will impact specimens in ancillary studies dependent on this study, // we need to force a participant/visit refresh in those study containers (if any): for (Study dependentStudy : StudyService.get().getAncillaryStudies(ctx.getContainer())) - VisitService.get().updateParticipantVisits(dependentStudy, ctx.getUser()); + { + ValidationException errors = VisitService.get().updateParticipantVisits(dependentStudy, ctx.getUser()); + if (errors.hasErrors()) + throw errors; + } } } From 0de8a307f468ed9da99382516bc52710738c5842 Mon Sep 17 00:00:00 2001 From: Trey Chadick Date: Wed, 17 Jan 2024 17:05:07 -0800 Subject: [PATCH 407/494] Use correct file root when testing against embedded distribution Original PR: https://github.com/LabKey/testAutomation/pull/1798 --- .../labkey/test/tests/CustomizeEmailTemplateTest.java | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/org/labkey/test/tests/CustomizeEmailTemplateTest.java b/src/org/labkey/test/tests/CustomizeEmailTemplateTest.java index 16dc4116..c8bd40c7 100644 --- a/src/org/labkey/test/tests/CustomizeEmailTemplateTest.java +++ b/src/org/labkey/test/tests/CustomizeEmailTemplateTest.java @@ -45,7 +45,6 @@ public class CustomizeEmailTemplateTest extends SpecimenBaseTest private static final String _shipping = "123 main street"; private static final String _comments = "this is my comment"; private static final String _studyName = "Study 001"; - private static final String _delim = "::"; private static final String _notificationDivName = "params"; private static final List replacementParams = Arrays.asList( "action", @@ -151,7 +150,7 @@ protected void doVerifySteps() throws Exception EmailRecordTable emailRecordTable = goToEmailRecord(); EmailRecordTable.EmailMessage message = emailRecordTable.getEmailAtTableIndex(3); emailRecordTable.clickMessage(message); - String[] bodyContents = Locator.name(_notificationDivName).findElement(getDriver()).getText().split(_delim); + String[] bodyContents = Locator.name(_notificationDivName).findElement(getDriver()).getText().split("\n"); Map emailNVPs = new HashMap<>(); for (String line : bodyContents) { @@ -168,7 +167,7 @@ protected void doVerifySteps() throws Exception assertEquals(_studyName, message.getSubject()); assertEquals("New Request", emailNVPs.get("status")); assertEquals("New Request Created", emailNVPs.get("action")); - assertEquals("/labkey", emailNVPs.get("contextPath")); + assertEquals(WebTestHelper.getContextPath(), emailNVPs.getOrDefault("contextPath", "")); assertEquals("My Study", emailNVPs.get("folderName")); assertEquals("/EmailTemplateProject/My Study", emailNVPs.get("folderPath")); assertEquals(WebTestHelper.getBaseURL(), emailNVPs.get("homePageURL")); @@ -178,14 +177,14 @@ private String buildTemplateBody() { String delimiter = ""; StringBuilder templateBuilder = new StringBuilder(); - templateBuilder.append("
"); + templateBuilder.append("
\n"); for (String param : replacementParams) { templateBuilder.append(delimiter); templateBuilder.append(String.format("^%s|%s==%%s^", param, param)); - delimiter = _delim + "\n"; + delimiter = "
\n"; } - templateBuilder.append("
"); + templateBuilder.append("\n
"); return templateBuilder.toString(); } From 6bee6f1425d0d1efbdd0dc9df80dffa305179cea Mon Sep 17 00:00:00 2001 From: labkey-matthewb Date: Fri, 26 Jan 2024 17:02:10 -0800 Subject: [PATCH 408/494] JavaScriptFragment.asJson() Original PR: https://github.com/LabKey/platform/pull/5108 * JavaScriptFragment.asJson() replace Jsp.unsafe() with JavaScriptFragment.unsafe() in - - -
<%= text(tdButtons) %> >
<%= h(type.getName())%><%= h(type.getDescription())%>  <%= h(type.getDescription())%>
- - - - - - - - -
pages (NYI)
 
filters
 
columns
 
rows
measures
 
- - - -
-
-

I'm a page header

-
-
From 4bd7e45a83fd109807ee5cd298b0fceb75e67baf Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Mon, 20 May 2024 15:00:33 -0700 Subject: [PATCH 415/494] Migrate DbCache use to DatabaseCache Original PR: https://github.com/LabKey/platform/pull/5472 --- .../org/labkey/specimen/SpecimenManager.java | 62 +++++ .../org/labkey/specimen/SpecimenModule.java | 13 - .../specimen/SpecimenRequestManager.java | 36 ++- .../labkey/specimen/SpecimenServiceImpl.java | 80 ++++++- .../actions/ShowUploadSpecimensAction.java | 3 +- .../actions/SpecimenApiController.java | 6 +- .../specimen/actions/SpecimenController.java | 80 ++++--- .../importer/SpecimenSettingsImporter.java | 14 +- .../specimen/model/SpecimenRequestEvent.java | 21 +- .../DefaultRequestNotification.java | 13 +- .../requirements/SpecimenRequest.java | 23 +- .../settings/RequestNotificationSettings.java | 226 ++++++++++++++++++ .../specimen/settings/SettingsManager.java | 103 ++++++++ ...cimenRequestNotificationEmailTemplate.java | 11 +- .../view/SpecimenToolsWebPartFactory.java | 2 +- .../specimen/view/manageNotifications.jsp | 7 +- .../src/org/labkey/specimen/view/webPart.jsp | 6 +- .../writer/SpecimenSettingsWriter.java | 4 +- 18 files changed, 607 insertions(+), 103 deletions(-) create mode 100644 specimen/src/org/labkey/specimen/settings/RequestNotificationSettings.java create mode 100644 specimen/src/org/labkey/specimen/settings/SettingsManager.java diff --git a/specimen/src/org/labkey/specimen/SpecimenManager.java b/specimen/src/org/labkey/specimen/SpecimenManager.java index 16463ee9..8165c3b5 100644 --- a/specimen/src/org/labkey/specimen/SpecimenManager.java +++ b/specimen/src/org/labkey/specimen/SpecimenManager.java @@ -25,6 +25,7 @@ import org.labkey.api.specimen.Vial; import org.labkey.api.specimen.location.LocationImpl; import org.labkey.api.specimen.location.LocationManager; +import org.labkey.api.specimen.model.PrimaryType; import org.labkey.api.specimen.model.SpecimenComment; import org.labkey.api.study.Cohort; import org.labkey.api.study.Study; @@ -411,4 +412,65 @@ private List getDerivativeTypes(final Container container, @Null forEachMap(map -> derivativeTypes.add(new DerivativeType(container, map))); return derivativeTypes; } + + public boolean isSpecimensEmpty(Container container, User user) + { + TableSelector selector = SpecimenManagerNew.get().getSpecimensSelector(container, user, null); + return !selector.exists(); + } + + public Vial getVial(Container container, User user, String globalUniqueId) + { + SimpleFilter filter = new SimpleFilter(new SimpleFilter.SQLClause("LOWER(GlobalUniqueId) = LOWER(?)", new Object[] { globalUniqueId })); + List matches = SpecimenManagerNew.get().getVials(container, user, filter); + if (matches == null || matches.isEmpty()) + return null; + if (matches.size() > 1) + { + // we apparently have two specimens with IDs that differ only in case; do a case-sensitive check + // here to find the right one: + for (Vial vial : matches) + { + if (vial.getGlobalUniqueId().equals(globalUniqueId)) + return vial; + } + throw new IllegalStateException("Expected at least one vial to exactly match the specified global unique ID: " + globalUniqueId); + } + else + return matches.get(0); + } + + /** Looks for any specimens that have the given id as a globalUniqueId */ + public PrimaryType getPrimaryType(Container c, int rowId) + { + List primaryTypes = SpecimenManagerNew.get().getPrimaryTypes(c, new SimpleFilter(FieldKey.fromParts("RowId"), rowId), null); + if (!primaryTypes.isEmpty()) + return primaryTypes.get(0); + return null; + } + + public List getRequestableVials(Container container, User user, Set vialRowIds) + { + SimpleFilter filter = SimpleFilter.createContainerFilter(container); + filter.addInClause(FieldKey.fromParts("RowId"), vialRowIds).addCondition(FieldKey.fromString("available"), true); + return SpecimenManagerNew.get().getVials(container, user, filter); + } + + public Map> getVialsForSpecimenHashes(Container container, User user, Collection hashes, boolean onlyAvailable) + { + SimpleFilter filter = SimpleFilter.createContainerFilter(container); + filter.addInClause(FieldKey.fromParts("SpecimenHash"), hashes); + if (onlyAvailable) + filter.addCondition(FieldKey.fromParts("Available"), true); + List vials = SpecimenManagerNew.get().getVials(container, user, filter); + Map> map = new HashMap<>(); + for (Vial vial : vials) + { + String hash = vial.getSpecimenHash(); + List keyVials = map.computeIfAbsent(hash, k -> new ArrayList<>()); + keyVials.add(vial); + } + + return map; + } } diff --git a/specimen/src/org/labkey/specimen/SpecimenModule.java b/specimen/src/org/labkey/specimen/SpecimenModule.java index b01650ee..01c6f48f 100644 --- a/specimen/src/org/labkey/specimen/SpecimenModule.java +++ b/specimen/src/org/labkey/specimen/SpecimenModule.java @@ -70,7 +70,6 @@ import org.labkey.specimen.pipeline.SampleMindedTransform; import org.labkey.specimen.pipeline.SampleMindedTransformTask; import org.labkey.specimen.pipeline.SpecimenPipeline; -import org.labkey.specimen.requirements.SpecimenRequestRequirementProvider; import org.labkey.specimen.security.roles.SpecimenCoordinatorRole; import org.labkey.specimen.security.roles.SpecimenRequesterRole; import org.labkey.specimen.view.ManageSpecimenView; @@ -201,23 +200,11 @@ public void clearRequestCaches(Container c) SpecimenRequestManager.get().clearCaches(c); } - @Override - public void clearGroupedValuesForColumn(Container container) - { - SpecimenRequestManager.get().clearGroupedValuesForColumn(container); - } - @Override public void updateVialCounts(Container container, User user) { SpecimenRequestManager.get().updateVialCounts(container, user); } - - @Override - public void purgeRequestRequirementsAndActors(Container container) - { - SpecimenRequestRequirementProvider.get().purgeContainer(container); - } }); } diff --git a/specimen/src/org/labkey/specimen/SpecimenRequestManager.java b/specimen/src/org/labkey/specimen/SpecimenRequestManager.java index ea24d5eb..2bcfff0f 100644 --- a/specimen/src/org/labkey/specimen/SpecimenRequestManager.java +++ b/specimen/src/org/labkey/specimen/SpecimenRequestManager.java @@ -10,6 +10,7 @@ import org.labkey.api.cache.BlockingCache; import org.labkey.api.cache.CacheLoader; import org.labkey.api.cache.CacheManager; +import org.labkey.api.cache.DbCache; import org.labkey.api.data.ColumnInfo; import org.labkey.api.data.Container; import org.labkey.api.data.DatabaseCache; @@ -99,7 +100,7 @@ private SpecimenRequestManager() public List getRequestStatuses(Container c, User user) { - List statuses = _requestStatusHelper.get(c, "SortOrder"); + List statuses = _requestStatusHelper.getList(c, "SortOrder"); // if the 'not-yet-submitted' status doesn't exist, create it here, with sort order -1, // so it's always first. if (statuses == null || statuses.isEmpty() || statuses.get(0).getSortOrder() != -1) @@ -113,8 +114,10 @@ public List getRequestStatuses(Container c, User user) try (var ignore = SpringActionController.ignoreSqlUpdates()) { Table.insert(user, _requestStatusHelper.getTableInfo(), notYetSubmittedStatus); + DbCache.trackRemove(_requestStatusHelper.getTableInfo()); + _requestStatusHelper.clearCache(c); } - statuses = _requestStatusHelper.get(c, "SortOrder"); + statuses = _requestStatusHelper.getList(c, "SortOrder"); } return statuses; } @@ -157,7 +160,7 @@ public boolean hasEditRequestPermissions(User user, SpecimenRequest request) public Set getRequestStatusIdsInUse(Container c) { - List requests = _requestHelper.get(c); + List requests = _requestHelper.getList(c); Set uniqueStatuses = new HashSet<>(); for (SpecimenRequest request : requests) uniqueStatuses.add(request.getStatusId()); @@ -166,7 +169,7 @@ public Set getRequestStatusIdsInUse(Container c) public List getRequestEvents(Container c) { - return _requestEventHelper.get(c); + return _requestEventHelper.getList(c); } public SpecimenRequestEvent getRequestEvent(Container c, int rowId) @@ -303,7 +306,7 @@ public SpecimenRequestInput[] getNewSpecimenRequestInputs(Container container, b String parentObjectLsid = getRequestInputObjectLsid(container); Map resourceProperties = OntologyManager.getPropertyObjects(container, parentObjectLsid); SpecimenRequestInput[] inputs = new SpecimenRequestInput[0]; - if (resourceProperties == null || resourceProperties.size() == 0) + if (resourceProperties == null || resourceProperties.isEmpty()) { if (createIfMissing) { @@ -547,7 +550,7 @@ public List getRequests(Container c, User user) SimpleFilter filter = new SimpleFilter(FieldKey.fromParts("Hidden"), Boolean.FALSE); if (user != null) filter.addCondition(FieldKey.fromParts("CreatedBy"), user.getUserId()); - return _requestHelper.get(c, filter, "-Created"); + return _requestHelper.getList(c, filter, "-Created"); } public SpecimenRequest getRequest(Container c, int rowId) @@ -748,7 +751,7 @@ private void updateVialCounts(Container container, User user, List vials) updateSql.append("\tFROM ").append(tableInfoVialSelectName).append("\n"); - if (vials != null && vials.size() > 0) + if (vials != null && !vials.isEmpty()) { Set specimenIds = new HashSet<>(); for (Vial vial : vials) @@ -789,6 +792,21 @@ public void clearCaches(Container c) clearGroupedValuesForColumn(c); } + void clearRequestStatusHelper(Container c) + { + _requestStatusHelper.clearCache(c); + } + + void clearRequestHelper(Container c) + { + _requestHelper.clearCache(c); + } + + void clearRequestEventHelper(Container c) + { + _requestEventHelper.clearCache(c); + } + private static class GroupedValueColumnHelper { private final String _viewColumnName; @@ -1232,7 +1250,7 @@ public List getMissingSpecimens(SpecimenRequest specimenRequest) private void deleteRequestEvents(SpecimenRequest request) { SimpleFilter filter = new SimpleFilter(FieldKey.fromParts("RequestId"), request.getRowId()); - List events = _requestEventHelper.get(request.getContainer(), filter); + List events = _requestEventHelper.getList(request.getContainer(), filter); for (SpecimenRequestEvent event : events) { AttachmentService.get().deleteAttachments(event); @@ -1242,7 +1260,7 @@ private void deleteRequestEvents(SpecimenRequest request) public RequestedSpecimens getRequestableBySpecimenHash(Container c, User user, Set formValues, Integer preferredLocation) throws AmbiguousLocationException { - Map> vialsByHash = SpecimenManagerNew.get().getVialsForSpecimenHashes(c, user, formValues, true); + Map> vialsByHash = SpecimenManager.get().getVialsForSpecimenHashes(c, user, formValues, true); if (vialsByHash == null || vialsByHash.isEmpty()) return new RequestedSpecimens(Collections.emptyList()); diff --git a/specimen/src/org/labkey/specimen/SpecimenServiceImpl.java b/specimen/src/org/labkey/specimen/SpecimenServiceImpl.java index 4f674491..6354073b 100644 --- a/specimen/src/org/labkey/specimen/SpecimenServiceImpl.java +++ b/specimen/src/org/labkey/specimen/SpecimenServiceImpl.java @@ -20,27 +20,34 @@ import org.apache.logging.log4j.Logger; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.labkey.api.cache.DbCache; import org.labkey.api.data.Container; +import org.labkey.api.data.DbSchema; import org.labkey.api.data.PropertyManager; import org.labkey.api.data.SQLFragment; +import org.labkey.api.data.SimpleFilter; import org.labkey.api.data.SqlSelector; +import org.labkey.api.data.Table; import org.labkey.api.data.TableInfo; import org.labkey.api.exp.Lsid; import org.labkey.api.exp.api.ExpMaterial; +import org.labkey.api.exp.api.ExpSampleType; import org.labkey.api.exp.api.ExperimentService; +import org.labkey.api.exp.api.SampleTypeService; import org.labkey.api.exp.property.Domain; import org.labkey.api.pipeline.PipeRoot; import org.labkey.api.pipeline.PipelineJob; import org.labkey.api.pipeline.PipelineService; +import org.labkey.api.query.FieldKey; import org.labkey.api.query.ValidationException; import org.labkey.api.security.User; import org.labkey.api.specimen.DefaultSpecimenTablesTemplate; import org.labkey.api.specimen.SpecimenColumns; -import org.labkey.api.specimen.SpecimenManagerNew; import org.labkey.api.specimen.SpecimenSchema; import org.labkey.api.specimen.Vial; import org.labkey.api.specimen.importer.SimpleSpecimenImporter; import org.labkey.api.specimen.importer.SpecimenColumn; +import org.labkey.api.specimen.location.LocationCache; import org.labkey.api.specimen.model.SpecimenTablesProvider; import org.labkey.api.study.ParticipantVisit; import org.labkey.api.study.SpecimenChangeListener; @@ -54,6 +61,7 @@ import org.labkey.api.view.ActionURL; import org.labkey.api.view.ViewBackgroundInfo; import org.labkey.specimen.pipeline.SpecimenReloadJob; +import org.labkey.specimen.requirements.SpecimenRequestRequirementProvider; import java.io.IOException; import java.util.ArrayList; @@ -211,7 +219,7 @@ public void setDate(Date date) @Override public ParticipantVisit getSampleInfo(Container studyContainer, User user, String sampleId) { - Vial match = SpecimenManagerNew.get().getVial(studyContainer, user, sampleId); + Vial match = SpecimenManager.get().getVial(studyContainer, user, sampleId); if (match != null) return new StudyParticipantVisit(studyContainer, sampleId, match.getPtid(), match.getVisitValue(), match.getDrawTimestamp()); else @@ -224,7 +232,7 @@ public Set getSampleInfo(Container studyContainer, User user, if (null != studyContainer && null != StringUtils.trimToNull(participantId) && null != date) { List matches = SpecimenManager.get().getVials(studyContainer, user, participantId, date); - if (matches.size() > 0) + if (!matches.isEmpty()) { Set result = new HashSet<>(); for (Vial match : matches) @@ -461,4 +469,70 @@ public void fireSpecimensChanged(Container c, User user, Logger logger) for (SpecimenChangeListener l : _changeListeners) l.specimensChanged(c, user, logger); } + + @Override + public void deleteAllSpecimenData(Container c, Set set, User user) + { + // UNDONE: use transaction? + SimpleFilter containerFilter = SimpleFilter.createContainerFilter(c); + + Table.delete(SpecimenSchema.get().getTableInfoSampleRequestSpecimen(), containerFilter); + assert set.add(SpecimenSchema.get().getTableInfoSampleRequestSpecimen()); + Table.delete(SpecimenSchema.get().getTableInfoSampleRequestEvent(), containerFilter); + DbCache.trackRemove(SpecimenSchema.get().getTableInfoSampleRequestEvent()); + SpecimenRequestManager.get().clearRequestEventHelper(c); + assert set.add(SpecimenSchema.get().getTableInfoSampleRequestEvent()); + Table.delete(SpecimenSchema.get().getTableInfoSampleRequest(), containerFilter); + DbCache.trackRemove(SpecimenSchema.get().getTableInfoSampleRequest()); + SpecimenRequestManager.get().clearRequestHelper(c); + assert set.add(SpecimenSchema.get().getTableInfoSampleRequest()); + Table.delete(SpecimenSchema.get().getTableInfoSampleRequestStatus(), containerFilter); + DbCache.trackRemove(SpecimenSchema.get().getTableInfoSampleRequestStatus()); + SpecimenRequestManager.get().clearRequestStatusHelper(c); + assert set.add(SpecimenSchema.get().getTableInfoSampleRequestStatus()); + + new SpecimenTablesProvider(c, null, null).deleteTables(); + LocationCache.clear(c); + + Table.delete(SpecimenSchema.get().getTableInfoSampleAvailabilityRule(), containerFilter); + assert set.add(SpecimenSchema.get().getTableInfoSampleAvailabilityRule()); + + SpecimenRequestRequirementProvider.get().purgeContainer(c); + assert set.add(SpecimenSchema.get().getTableInfoSampleRequestRequirement()); + assert set.add(SpecimenSchema.get().getTableInfoSampleRequestActor()); + + DbSchema expSchema = ExperimentService.get().getSchema(); + TableInfo tinfoMaterial = expSchema.getTable("Material"); + + ExpSampleType sampleType = SampleTypeService.get().getSampleType(c, SpecimenService.SAMPLE_TYPE_NAME); + + if (sampleType != null) + { + // Check if any of the samples are referenced in an experiment run + SQLFragment sql = new SQLFragment("SELECT m.RowId FROM "); + sql.append(ExperimentService.get().getTinfoMaterial(), "m"); + sql.append(" INNER JOIN "); + sql.append(ExperimentService.get().getTinfoMaterialInput(), "mi"); + sql.append(" ON m.RowId = mi.MaterialId AND m.CpasType = ?"); + sql.add(sampleType.getLSID()); + + if (new SqlSelector(ExperimentService.get().getSchema(), sql).exists()) + { + // If so, do the slow version of the delete that tears down runs + sampleType.delete(user); + } + else + { + // If not, do the quick version that just kills the samples themselves in the exp.Material table + SimpleFilter materialFilter = new SimpleFilter(containerFilter); + materialFilter.addCondition(FieldKey.fromParts("CpasType"), sampleType.getLSID()); + Table.delete(tinfoMaterial, materialFilter); + } + } + + // VIEW: if this view gets removed, remove this line + assert set.add(SpecimenSchema.get().getSchema().getTable("LockedSpecimens")); + + SpecimenRequestManager.get().clearGroupedValuesForColumn(c); + } } diff --git a/specimen/src/org/labkey/specimen/actions/ShowUploadSpecimensAction.java b/specimen/src/org/labkey/specimen/actions/ShowUploadSpecimensAction.java index ffe32443..9f7a40d8 100644 --- a/specimen/src/org/labkey/specimen/actions/ShowUploadSpecimensAction.java +++ b/specimen/src/org/labkey/specimen/actions/ShowUploadSpecimensAction.java @@ -48,6 +48,7 @@ import org.labkey.api.view.HttpView; import org.labkey.api.view.JspView; import org.labkey.api.view.NavTree; +import org.labkey.specimen.SpecimenManager; import org.springframework.validation.BindException; import org.springframework.validation.Errors; import org.springframework.web.servlet.ModelAndView; @@ -78,7 +79,7 @@ public ModelAndView getView(UploadSpecimensForm form, boolean reshow, BindExcept if (!settings.isSimple()) return HttpView.redirect(PageFlowUtil. urlProvider(PipelineUrls.class).urlBrowse(container)); - boolean isEmpty = SpecimenManagerNew.get().isSpecimensEmpty(container, getUser()); + boolean isEmpty = SpecimenManager.get().isSpecimensEmpty(container, getUser()); if (isEmpty) { form.setNoSpecimens(true); diff --git a/specimen/src/org/labkey/specimen/actions/SpecimenApiController.java b/specimen/src/org/labkey/specimen/actions/SpecimenApiController.java index c119ce40..e091c857 100644 --- a/specimen/src/org/labkey/specimen/actions/SpecimenApiController.java +++ b/specimen/src/org/labkey/specimen/actions/SpecimenApiController.java @@ -126,7 +126,7 @@ private List> getSpecimenListResponse(List vials) vialProperties.put("primaryTypeId", vial.getPrimaryTypeId()); if (vial.getPrimaryTypeId() != null) { - PrimaryType primaryType = SpecimenManagerNew.get().getPrimaryType(vial.getContainer(), vial.getPrimaryTypeId()); + PrimaryType primaryType = SpecimenManager.get().getPrimaryType(vial.getContainer(), vial.getPrimaryTypeId()); if (primaryType != null) vialProperties.put("primaryType", primaryType.getPrimaryType()); } @@ -331,7 +331,7 @@ public class GetProvidingLocations extends ReadOnlyApiAction> vialsByHash = SpecimenManagerNew.get().getVialsForSpecimenHashes(getContainer(), getUser(), + Map> vialsByHash = SpecimenManager.get().getVialsForSpecimenHashes(getContainer(), getUser(), PageFlowUtil.set(form.getSpecimenHashes()), true); Collection preferredLocations = StudyUtils.getPreferredProvidingLocations(vialsByHash.values()); final Map response = new HashMap<>(); @@ -486,7 +486,7 @@ private Vial getVial(String vialId, String idType) { Vial vial; if (IdTypes.GlobalUniqueId.name().equals(idType)) - vial = SpecimenManagerNew.get().getVial(getContainer(), getUser(), vialId); + vial = SpecimenManager.get().getVial(getContainer(), getUser(), vialId); else if (IdTypes.RowId.name().equals(idType)) { try diff --git a/specimen/src/org/labkey/specimen/actions/SpecimenController.java b/specimen/src/org/labkey/specimen/actions/SpecimenController.java index e43e6ecc..c8491c6b 100644 --- a/specimen/src/org/labkey/specimen/actions/SpecimenController.java +++ b/specimen/src/org/labkey/specimen/actions/SpecimenController.java @@ -1,5 +1,12 @@ package org.labkey.specimen.actions; +import jakarta.mail.Address; +import jakarta.mail.Message; +import jakarta.mail.internet.AddressException; +import jakarta.mail.internet.InternetAddress; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpSession; import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -25,7 +32,30 @@ import org.labkey.api.attachments.BaseDownloadAction; import org.labkey.api.attachments.ByteArrayAttachmentFile; import org.labkey.api.audit.TransactionAuditProvider; -import org.labkey.api.data.*; +import org.labkey.api.data.ActionButton; +import org.labkey.api.data.BaseColumnInfo; +import org.labkey.api.data.BeanViewForm; +import org.labkey.api.data.ButtonBar; +import org.labkey.api.data.ColumnInfo; +import org.labkey.api.data.CompareType; +import org.labkey.api.data.Container; +import org.labkey.api.data.DataColumn; +import org.labkey.api.data.DataRegion; +import org.labkey.api.data.DataRegionSelection; +import org.labkey.api.data.DbScope; +import org.labkey.api.data.DisplayColumn; +import org.labkey.api.data.ExcelWriter; +import org.labkey.api.data.MenuButton; +import org.labkey.api.data.ObjectFactory; +import org.labkey.api.data.PropertyManager; +import org.labkey.api.data.RenderContext; +import org.labkey.api.data.SimpleDisplayColumn; +import org.labkey.api.data.SimpleFilter; +import org.labkey.api.data.Sort; +import org.labkey.api.data.TSVGridWriter; +import org.labkey.api.data.TSVWriter; +import org.labkey.api.data.Table; +import org.labkey.api.data.TableInfo; import org.labkey.api.gwt.client.AuditBehaviorType; import org.labkey.api.module.FolderType; import org.labkey.api.module.Module; @@ -47,7 +77,6 @@ import org.labkey.api.reader.DataLoader; import org.labkey.api.security.ActionNames; import org.labkey.api.security.RequiresPermission; -import org.labkey.api.security.RequiresSiteAdmin; import org.labkey.api.security.User; import org.labkey.api.security.ValidEmail; import org.labkey.api.security.permissions.AdminPermission; @@ -70,7 +99,6 @@ import org.labkey.api.specimen.security.permissions.RequestSpecimensPermission; import org.labkey.api.specimen.settings.DisplaySettings; import org.labkey.api.specimen.settings.RepositorySettings; -import org.labkey.api.specimen.settings.RequestNotificationSettings; import org.labkey.api.specimen.settings.SettingsManager; import org.labkey.api.specimen.settings.StatusSettings; import org.labkey.api.study.CohortFilter; @@ -147,6 +175,7 @@ import org.labkey.specimen.security.permissions.ManageRequestsPermission; import org.labkey.specimen.security.permissions.ManageSpecimenActorsPermission; import org.labkey.specimen.security.permissions.SetSpecimenCommentsPermission; +import org.labkey.specimen.settings.RequestNotificationSettings; import org.labkey.specimen.view.NotificationBean; import org.labkey.specimen.view.SpecimenRequestNotificationEmailTemplate; import org.labkey.specimen.view.SpecimenSearchWebPart; @@ -157,13 +186,6 @@ import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.mvc.Controller; -import jakarta.mail.Address; -import jakarta.mail.Message; -import jakarta.mail.internet.AddressException; -import jakarta.mail.internet.InternetAddress; -import jakarta.servlet.ServletException; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpSession; import java.io.BufferedOutputStream; import java.io.ByteArrayOutputStream; import java.io.File; @@ -335,7 +357,7 @@ private ActionURL getManageStudyURL() public void ensureSpecimenRequestsConfigured(boolean checkExistingStatuses) { - if (!SettingsManager.get().isSpecimenRequestEnabled(getContainer(), checkExistingStatuses)) + if (!org.labkey.specimen.settings.SettingsManager.get().isSpecimenRequestEnabled(getContainer(), checkExistingStatuses)) throw new RedirectException(new ActionURL(SpecimenRequestConfigRequiredAction.class, getContainer())); } @@ -859,7 +881,7 @@ public ApiResponse execute(SpecimenWebPartForm form, BindException errors) groupings.add(form.getGrouping1()); groupings.add(form.getGrouping2()); settings.setSpecimenWebPartGroupings(groupings); - SettingsManager.get().saveRepositorySettings(container, settings); + org.labkey.specimen.settings.SettingsManager.get().saveRepositorySettings(container, settings); response.put("success", true); return response; } @@ -1071,7 +1093,7 @@ public ModelAndView getView(PipelineForm form, BindException bindErrors) } ImportSpecimensBean bean = new ImportSpecimensBean(getContainer(), archives, form.getPath(), form.getFile(), errors); - boolean isEmpty = SpecimenManagerNew.get().isSpecimensEmpty(getContainer(), getUser()); + boolean isEmpty = SpecimenManager.get().isSpecimensEmpty(getContainer(), getUser()); if (isEmpty) { bean.setNoSpecimens(true); @@ -1157,7 +1179,7 @@ public ModelAndView getView(SpecimenEventForm form, BindException errors) { if (form.getId() != null && form.getTargetStudy() != null) { - Vial vial = SpecimenManagerNew.get().getVial(form.getTargetStudy(), getUser(), form.getId()); + Vial vial = SpecimenManager.get().getVial(form.getTargetStudy(), getUser(), form.getId()); if (vial != null) { ActionURL url = new ActionURL(SpecimenEventsAction.class, form.getTargetStudy()).addParameter("id", vial.getRowId()); @@ -1464,7 +1486,7 @@ public ModelAndView getView(RequestNotificationSettings form, boolean reshow, Bi // try to get the settings from the form, just in case this is a reshow: RequestNotificationSettings settings = form; if (settings == null || settings.getReplyTo() == null) - settings = SettingsManager.get().getRequestNotificationSettings(getContainer()); + settings = org.labkey.specimen.settings.SettingsManager.get().getRequestNotificationSettings(getContainer()); return new JspView<>("/org/labkey/specimen/view/manageNotifications.jsp", settings, errors); } @@ -1491,7 +1513,7 @@ public boolean handlePost(RequestNotificationSettings settings, BindException er if (errors.hasErrors()) return false; - SettingsManager.get().saveRequestNotificationSettings(getContainer(), settings); + org.labkey.specimen.settings.SettingsManager.get().saveRequestNotificationSettings(getContainer(), settings); return true; } @@ -1533,7 +1555,7 @@ public ModelAndView getView(DisplaySettingsForm form, boolean reshow, BindExcept public boolean handlePost(DisplaySettingsForm form, BindException errors) { DisplaySettings settings = form.getBean(); - SettingsManager.get().saveDisplaySettings(getContainer(), settings); + org.labkey.specimen.settings.SettingsManager.get().saveDisplaySettings(getContainer(), settings); return true; } @@ -1654,7 +1676,7 @@ public boolean handlePost(ManageRepositorySettingsForm form, BindException error settings.setSimple(form.isSimple()); settings.setEnableRequests(!form.isSimple() && form.isEnableRequests()); settings.setSpecimenDataEditable(!form.isSimple() && form.isSpecimenDataEditable()); - SettingsManager.get().saveRepositorySettings(getContainer(), settings); + org.labkey.specimen.settings.SettingsManager.get().saveRepositorySettings(getContainer(), settings); return true; } @@ -1852,7 +1874,7 @@ public boolean handlePost(DefaultRequirementsForm form, BindException errors) private void createDefaultRequirement(Integer actorId, String description, SpecimenRequestRequirementType type) { - if (actorId != null && actorId.intValue() > 0 && description != null && description.length() > 0) + if (actorId != null && actorId.intValue() > 0 && description != null && !description.isEmpty()) { SpecimenRequestRequirement requirement = new SpecimenRequestRequirement(); requirement.setContainer(getContainer()); @@ -1866,7 +1888,7 @@ private void createDefaultRequirement(Integer actorId, String description, Speci @Override public ActionURL getSuccessURL(DefaultRequirementsForm form) { - if (form.getNextPage() != null && form.getNextPage().length() > 0) + if (form.getNextPage() != null && !form.getNextPage().isEmpty()) return new ActionURL(form.getNextPage()); else return getManageStudyURL(); @@ -2007,7 +2029,7 @@ public List getSpecimensFromPost(boolean fromGroupedView, boolean onlyAvai if (fromGroupedView) { Map> keyToVialMap = - SpecimenManagerNew.get().getVialsForSpecimenHashes(getContainer(), getUser(), formValues, onlyAvailable); + SpecimenManager.get().getVialsForSpecimenHashes(getContainer(), getUser(), formValues, onlyAvailable); List vials = new ArrayList<>(); for (List vialList : keyToVialMap.values()) vials.addAll(vialList); @@ -2216,7 +2238,7 @@ private RequestedSpecimens getRequestableByVialRowIds(Set rowIds, Contai { Set ids = new HashSet<>(); Arrays.stream(toLongArray(rowIds)).forEach(ids::add); - List requestedSpecimens = SpecimenManagerNew.get().getRequestableVials(container, user, ids); + List requestedSpecimens = SpecimenManager.get().getRequestableVials(container, user, ids); return new RequestedSpecimens(requestedSpecimens); } @@ -2229,7 +2251,7 @@ private RequestedSpecimens getRequestableByVialGlobalUniqueIds(Set globa List vials = new ArrayList<>(); for (String globalUniqueId : globalUniqueIds) { - Vial match = SpecimenManagerNew.get().getVial(container, user, globalUniqueId); + Vial match = SpecimenManager.get().getVial(container, user, globalUniqueId); if (match != null) vials.add(match); } @@ -3034,7 +3056,7 @@ public boolean handlePost(StatusEditForm form, BindException errors) if (settings.isUseShoppingCart() != form.isUseShoppingCart()) { settings.setUseShoppingCart(form.isUseShoppingCart()); - SettingsManager.get().saveStatusSettings(getContainer(), settings); + org.labkey.specimen.settings.SettingsManager.get().saveStatusSettings(getContainer(), settings); } return true; } @@ -3042,7 +3064,7 @@ public boolean handlePost(StatusEditForm form, BindException errors) @Override public ActionURL getSuccessURL(StatusEditForm form) { - if (form.getNextPage() != null && form.getNextPage().length() > 0) + if (form.getNextPage() != null && !form.getNextPage().isEmpty()) return new ActionURL(form.getNextPage()); else return getManageStudyURL(); @@ -3561,7 +3583,7 @@ protected int importData(DataLoader dl, FileStream file, String originalName, Ba boolean hasSpecimenError = false; for (String id : globalIds) { - Vial vial = SpecimenManagerNew.get().getVial(getContainer(), getUser(), id); + Vial vial = SpecimenManager.get().getVial(getContainer(), getUser(), id); if (vial == null) { errorList.add("Specimen " + id + " not found."); @@ -3672,7 +3694,7 @@ private List getNotifications(SpecimenReques private void sendNewRequestNotifications(SpecimenRequest request, BindException errors) throws Exception { - RequestNotificationSettings settings = SettingsManager.get().getRequestNotificationSettings(request.getContainer()); + RequestNotificationSettings settings = org.labkey.specimen.settings.SettingsManager.get().getRequestNotificationSettings(request.getContainer()); Address[] notify = settings.getNewRequestNotifyAddresses(); if (notify != null && notify.length > 0) { @@ -3686,7 +3708,7 @@ private void sendNewRequestNotifications(SpecimenRequest request, BindException private void sendNotification(DefaultRequestNotification notification, boolean includeInactiveUsers, BindException errors) throws Exception { - RequestNotificationSettings settings = SettingsManager.get().getRequestNotificationSettings(getContainer()); + RequestNotificationSettings settings = org.labkey.specimen.settings.SettingsManager.get().getRequestNotificationSettings(getContainer()); SpecimenRequest specimenRequest = notification.getSpecimenRequest(); String specimenList = null; @@ -4622,7 +4644,7 @@ public ManageRequirementBean(ViewContext context, SpecimenRequest request, Speci public boolean isDefaultNotification(ActorNotificationRecipientSet notification) { - RequestNotificationSettings settings = SettingsManager.get().getRequestNotificationSettings(getContainer()); + RequestNotificationSettings settings = org.labkey.specimen.settings.SettingsManager.get().getRequestNotificationSettings(getContainer()); if (settings.getDefaultEmailNotifyEnum() == RequestNotificationSettings.DefaultEmailNotifyEnum.All) return true; // All should be checked else if (settings.getDefaultEmailNotifyEnum() == RequestNotificationSettings.DefaultEmailNotifyEnum.None) diff --git a/specimen/src/org/labkey/specimen/importer/SpecimenSettingsImporter.java b/specimen/src/org/labkey/specimen/importer/SpecimenSettingsImporter.java index c2389d48..4fd362a0 100644 --- a/specimen/src/org/labkey/specimen/importer/SpecimenSettingsImporter.java +++ b/specimen/src/org/labkey/specimen/importer/SpecimenSettingsImporter.java @@ -26,7 +26,6 @@ import org.labkey.api.specimen.location.LocationImpl; import org.labkey.api.specimen.settings.DisplaySettings; import org.labkey.api.specimen.settings.RepositorySettings; -import org.labkey.api.specimen.settings.RequestNotificationSettings; import org.labkey.api.specimen.settings.SettingsManager; import org.labkey.api.specimen.settings.StatusSettings; import org.labkey.api.study.StudyService; @@ -42,6 +41,7 @@ import org.labkey.specimen.requirements.SpecimenRequestRequirement; import org.labkey.specimen.requirements.SpecimenRequestRequirementProvider; import org.labkey.specimen.requirements.SpecimenRequestRequirementType; +import org.labkey.specimen.settings.RequestNotificationSettings; import org.labkey.specimen.writer.SpecimenArchiveDataTypes; import org.labkey.study.xml.DefaultRequirementType; import org.labkey.study.xml.DefaultRequirementsType; @@ -160,7 +160,7 @@ private void importSettings(SimpleStudyImportContext ctx, SpecimenSettingsType x reposSettings.setEnableRequests(xmlSettings.getEnableRequests()); if (xmlSettings.isSetEditableRepository()) reposSettings.setSpecimenDataEditable(xmlSettings.getEditableRepository()); - SettingsManager.get().saveRepositorySettings(c, reposSettings); + org.labkey.specimen.settings.SettingsManager.get().saveRepositorySettings(c, reposSettings); // location types SpecimenSettingsType.LocationTypes xmlLocationTypes = xmlSettings.getLocationTypes(); @@ -234,7 +234,7 @@ else if (newStatusLabel != null) if (settings.isUseShoppingCart() != xmlRequestStatuses.getMultipleSearch()) { settings.setUseShoppingCart(xmlRequestStatuses.getMultipleSearch()); - SettingsManager.get().saveStatusSettings(ctx.getContainer(), settings); + org.labkey.specimen.settings.SettingsManager.get().saveStatusSettings(ctx.getContainer(), settings); } } } @@ -350,12 +350,12 @@ private void importDefaultRequirements(SimpleStudyImportContext ctx, SpecimenSet private void createDefaultRequirement(DefaultRequirementsType xmlReqs, SimpleStudyImportContext ctx, RequirementType type) { - if (xmlReqs != null && xmlReqs.getRequirementArray().length > 0) + if (xmlReqs != null) { for (DefaultRequirementType xmlReq : xmlReqs.getRequirementArray()) { List matchingActors = SpecimenRequestRequirementProvider.get().getActorsByLabel(ctx.getContainer(), xmlReq.getActor()); - if (matchingActors.size() > 0) + if (!matchingActors.isEmpty()) { SpecimenRequestRequirement requirement = new SpecimenRequestRequirement(); requirement.setContainer(ctx.getContainer()); @@ -392,7 +392,7 @@ private void importDisplaySettings(SimpleStudyImportContext ctx, SpecimenSetting display.setZeroVials(warnings.getZeroVials()); } - SettingsManager.get().saveDisplaySettings(ctx.getContainer(), display); + org.labkey.specimen.settings.SettingsManager.get().saveDisplaySettings(ctx.getContainer(), display); } } @@ -461,7 +461,7 @@ private void importNotifications(SimpleStudyImportContext ctx, SpecimenSettingsT if (xmlNotifications.getSpecimensAttachment() != null) notifications.setSpecimensAttachment(xmlNotifications.getSpecimensAttachment()); - SettingsManager.get().saveRequestNotificationSettings(ctx.getContainer(), notifications); + org.labkey.specimen.settings.SettingsManager.get().saveRequestNotificationSettings(ctx.getContainer(), notifications); } } diff --git a/specimen/src/org/labkey/specimen/model/SpecimenRequestEvent.java b/specimen/src/org/labkey/specimen/model/SpecimenRequestEvent.java index 2549c382..1f826ee6 100644 --- a/specimen/src/org/labkey/specimen/model/SpecimenRequestEvent.java +++ b/specimen/src/org/labkey/specimen/model/SpecimenRequestEvent.java @@ -23,12 +23,8 @@ import org.labkey.api.study.AbstractStudyCachable; import java.util.Date; +import java.util.Objects; -/** - * User: brittp - * Date: Feb 24, 2006 - * Time: 1:56:09 PM - */ public class SpecimenRequestEvent extends AbstractStudyCachable implements AttachmentParent { private int _rowId; @@ -160,4 +156,19 @@ public void setRequirementId(Integer requirementId) { return SpecimenRequestEventType.get(); } + + @Override + public boolean equals(Object o) + { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + SpecimenRequestEvent that = (SpecimenRequestEvent) o; + return _rowId == that._rowId && _createdBy == that._createdBy && _created == that._created && _requestId == that._requestId && Objects.equals(_entityId, that._entityId) && Objects.equals(_container, that._container) && Objects.equals(_requirementId, that._requirementId) && Objects.equals(_comments, that._comments) && Objects.equals(_entryType, that._entryType); + } + + @Override + public int hashCode() + { + return Objects.hash(_rowId, _entityId, _createdBy, _created, _container, _requestId, _requirementId, _comments, _entryType); + } } diff --git a/specimen/src/org/labkey/specimen/notifications/DefaultRequestNotification.java b/specimen/src/org/labkey/specimen/notifications/DefaultRequestNotification.java index 6d7ef033..b838346b 100644 --- a/specimen/src/org/labkey/specimen/notifications/DefaultRequestNotification.java +++ b/specimen/src/org/labkey/specimen/notifications/DefaultRequestNotification.java @@ -26,23 +26,18 @@ import org.labkey.api.data.TSVWriter; import org.labkey.api.specimen.Vial; import org.labkey.api.specimen.query.SpecimenQueryView; -import org.labkey.api.specimen.settings.RequestNotificationSettings; -import org.labkey.api.specimen.settings.SettingsManager; import org.labkey.api.view.ViewContext; import org.labkey.specimen.model.SpecimenRequestEvent; import org.labkey.specimen.requirements.SpecimenRequest; import org.labkey.specimen.requirements.SpecimenRequestRequirement; +import org.labkey.specimen.settings.RequestNotificationSettings; +import org.labkey.specimen.settings.SettingsManager; import java.io.IOException; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; -/** - * User: brittp - * Date: May 4, 2007 - * Time: 3:41:44 PM - */ public class DefaultRequestNotification { protected List _recipients; @@ -68,7 +63,7 @@ public DefaultRequestNotification(SpecimenRequest request, List vials = getSpecimenList(); - if (vials != null && vials.size() > 0) + if (vials != null && !vials.isEmpty()) { SpecimenQueryView view = SpecimenQueryView.createView(context, vials, SpecimenQueryView.ViewType.VIALS_EMAIL); view.setDisableLowVialIndicators(true); @@ -86,7 +81,7 @@ private void addSpecimenListFileIfNeeded(ViewContext context) throws Exception { final ByteArrayAttachmentFile specimenListFile; List vials = getSpecimenList(); - if (vials != null && vials.size() > 0) + if (vials != null && !vials.isEmpty()) { SpecimenQueryView view = SpecimenQueryView.createView(context, vials, SpecimenQueryView.ViewType.VIALS_EMAIL); view.getSettings().setMaxRows(-1); diff --git a/specimen/src/org/labkey/specimen/requirements/SpecimenRequest.java b/specimen/src/org/labkey/specimen/requirements/SpecimenRequest.java index c59f9fe7..fc6023f0 100644 --- a/specimen/src/org/labkey/specimen/requirements/SpecimenRequest.java +++ b/specimen/src/org/labkey/specimen/requirements/SpecimenRequest.java @@ -30,12 +30,8 @@ import java.util.ArrayList; import java.util.Date; import java.util.List; +import java.util.Objects; -/** - * User: brittp - * Date: Feb 8, 2006 - * Time: 1:50:53 PM - */ public class SpecimenRequest extends AbstractStudyCachable implements RequirementOwner { private Container _container; @@ -47,7 +43,7 @@ public class SpecimenRequest extends AbstractStudyCachable impl private int _modifiedBy; private long _modified; private String _comments; - private Integer _destinationSiteId; // This is a locationId, but still needs to martch the column in the table + private Integer _destinationSiteId; // This is a locationId, but still needs to match the column in the table private boolean _hidden; @Override @@ -222,4 +218,19 @@ public void setEntityId(String entityId) { _entityId = entityId; } + + @Override + public boolean equals(Object o) + { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + SpecimenRequest that = (SpecimenRequest) o; + return _rowId == that._rowId && _statusId == that._statusId && _createdBy == that._createdBy && _created == that._created && _modifiedBy == that._modifiedBy && _modified == that._modified && _hidden == that._hidden && Objects.equals(_container, that._container) && Objects.equals(_entityId, that._entityId) && Objects.equals(_comments, that._comments) && Objects.equals(_destinationSiteId, that._destinationSiteId); + } + + @Override + public int hashCode() + { + return Objects.hash(_container, _entityId, _rowId, _statusId, _createdBy, _created, _modifiedBy, _modified, _comments, _destinationSiteId, _hidden); + } } diff --git a/specimen/src/org/labkey/specimen/settings/RequestNotificationSettings.java b/specimen/src/org/labkey/specimen/settings/RequestNotificationSettings.java new file mode 100644 index 00000000..10ed391e --- /dev/null +++ b/specimen/src/org/labkey/specimen/settings/RequestNotificationSettings.java @@ -0,0 +1,226 @@ +/* + * 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.specimen.settings; + +import jakarta.mail.Address; +import org.labkey.api.data.Container; +import org.labkey.api.security.User; +import org.labkey.api.security.ValidEmail; +import org.labkey.api.settings.LookAndFeelProperties; +import org.labkey.api.util.SafeToRenderEnum; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.StringTokenizer; + +public class RequestNotificationSettings +{ + public static final String REPLY_TO_CURRENT_USER_VALUE = "[current user]"; + private static final String KEY_REPLYTO = "ReplyTo"; + private static final String KEY_CC = "CC"; + private static final String KEY_SUBJECTSUFFIX = "SubjectSuffix"; + private static final String KEY_NEWREQUESTNOTIFY = "NewRequestNotify"; + private static final String KEY_DEFAULTEMAILNOTIFY = "DefaultEmailNotify"; + private static final String KEY_SPECIMENSATTACHMENT = "SpecimensAttachment"; + private String _replyTo; + private String _cc; + private String _subjectSuffix; + private String _newRequestNotify; + private boolean _ccCheckbox; + private boolean _newRequestNotifyCheckbox; + private DefaultEmailNotifyEnum _defaultEmailNotify = DefaultEmailNotifyEnum.ActorsInvolved; + private SpecimensAttachmentEnum _specimensAttachment = SpecimensAttachmentEnum.InEmailBody; + + public enum DefaultEmailNotifyEnum implements SafeToRenderEnum + {All, None, ActorsInvolved} + public enum SpecimensAttachmentEnum implements SafeToRenderEnum + {InEmailBody, ExcelAttachment, TextAttachment, Never} + + public RequestNotificationSettings() + { + // no-arg constructor for struts reflection + } + + public RequestNotificationSettings(Map map) + { + _replyTo = map.get(KEY_REPLYTO); + _cc = map.get(KEY_CC); + _subjectSuffix = map.get(KEY_SUBJECTSUFFIX); + _newRequestNotify = map.get(KEY_NEWREQUESTNOTIFY); + setDefaultEmailNotify(map.get(KEY_DEFAULTEMAILNOTIFY)); + setSpecimensAttachment(map.get(KEY_SPECIMENSATTACHMENT)); + } + + public String getReplyToEmailAddress(User currentAdmin) + { + if (RequestNotificationSettings.REPLY_TO_CURRENT_USER_VALUE.equals(getReplyTo())) + return currentAdmin.getEmail(); + else + return getReplyTo(); + } + + public String getReplyTo() + { + return _replyTo; + } + + public void setReplyTo(String replyTo) + { + _replyTo = replyTo; + } + + public String getCc() + { + return _cc; + } + + public void setCc(String cc) + { + _cc = cc; + } + + public String getSubjectSuffix() + { + return _subjectSuffix; + } + + public void setSubjectSuffix(String subjectSuffix) + { + _subjectSuffix = subjectSuffix; + } + + public String getNewRequestNotify() + { + return _newRequestNotify; + } + + public void setNewRequestNotify(String newRequestNotify) + { + _newRequestNotify = newRequestNotify; + } + + public boolean isCcCheckbox() + { + return _ccCheckbox; + } + + public void setCcCheckbox(boolean ccCheckbox) + { + _ccCheckbox = ccCheckbox; + } + + public boolean isNewRequestNotifyCheckbox() + { + return _newRequestNotifyCheckbox; + } + + public void setNewRequestNotifyCheckbox(boolean newRequestNotifyCheckbox) + { + _newRequestNotifyCheckbox = newRequestNotifyCheckbox; + } + + public void populateMap(Map map) + { + map.put(KEY_REPLYTO, _replyTo); + map.put(KEY_CC, _cc); + map.put(KEY_SUBJECTSUFFIX, _subjectSuffix); + map.put(KEY_NEWREQUESTNOTIFY, _newRequestNotify); + map.put(KEY_DEFAULTEMAILNOTIFY, getDefaultEmailNotify()); + map.put(KEY_SPECIMENSATTACHMENT, getSpecimensAttachment()); + } + + public static RequestNotificationSettings getDefaultSettings(Container c) + { + RequestNotificationSettings defaults = new RequestNotificationSettings(); + defaults.setReplyTo(LookAndFeelProperties.getInstance(c).getSystemEmailAddress()); + defaults.setCc(""); + defaults.setNewRequestNotify(""); + defaults.setSubjectSuffix("Specimen Request Notification"); + return defaults; + } + + public Address[] getCCAddresses() throws ValidEmail.InvalidEmailException + { + if (_cc == null || _cc.length() == 0) + return null; + StringTokenizer splitter = new StringTokenizer(_cc, ",;:\t\n\r"); + List
addresses = new ArrayList<>(); + while (splitter.hasMoreTokens()) + { + String token = splitter.nextToken(); + ValidEmail tester = new ValidEmail(token); + addresses.add(tester.getAddress()); + } + return addresses.toArray(new Address[addresses.size()]); + } + + public Address[] getNewRequestNotifyAddresses() throws ValidEmail.InvalidEmailException + { + if (_newRequestNotify == null || _newRequestNotify.length() == 0) + return null; + StringTokenizer splitter = new StringTokenizer(_newRequestNotify, ",;:\t\n\r"); + List
addresses = new ArrayList<>(); + while (splitter.hasMoreTokens()) + { + String token = splitter.nextToken(); + ValidEmail tester = new ValidEmail(token); + addresses.add(tester.getAddress()); + } + return addresses.toArray(new Address[addresses.size()]); + } + + public boolean isReplyToCurrentUser() + { + return RequestNotificationSettings.REPLY_TO_CURRENT_USER_VALUE.equals(getReplyTo()); + } + + public String getDefaultEmailNotify() + { + return _defaultEmailNotify.name(); + } + + public DefaultEmailNotifyEnum getDefaultEmailNotifyEnum() + { + return _defaultEmailNotify; + } + + public void setDefaultEmailNotify(String defaultEmailNotify) + { + if (null == defaultEmailNotify) + _defaultEmailNotify = DefaultEmailNotifyEnum.ActorsInvolved; + else + _defaultEmailNotify = DefaultEmailNotifyEnum.valueOf(defaultEmailNotify); + } + + public String getSpecimensAttachment() + { + return _specimensAttachment.name(); + } + + public SpecimensAttachmentEnum getSpecimensAttachmentEnum() + { + return _specimensAttachment; + } + + public void setSpecimensAttachment(String specimensAttachment) + { + if (null == specimensAttachment) + _specimensAttachment = SpecimensAttachmentEnum.InEmailBody; + else + _specimensAttachment = SpecimensAttachmentEnum.valueOf(specimensAttachment); + } +} \ No newline at end of file diff --git a/specimen/src/org/labkey/specimen/settings/SettingsManager.java b/specimen/src/org/labkey/specimen/settings/SettingsManager.java new file mode 100644 index 00000000..6adfb2d8 --- /dev/null +++ b/specimen/src/org/labkey/specimen/settings/SettingsManager.java @@ -0,0 +1,103 @@ +package org.labkey.specimen.settings; + +import org.labkey.api.action.SpringActionController; +import org.labkey.api.data.Container; +import org.labkey.api.data.PropertyManager; +import org.labkey.api.security.UserManager; +import org.labkey.api.specimen.SpecimenRequestStatus; +import org.labkey.api.specimen.SpecimenSchema; +import org.labkey.api.specimen.settings.DisplaySettings; +import org.labkey.api.specimen.settings.RepositorySettings; +import org.labkey.api.specimen.settings.StatusSettings; +import org.labkey.api.study.QueryHelper; +import org.labkey.specimen.SpecimenRequestManager; + +import java.util.List; +import java.util.Map; + +public class SettingsManager +{ + private final QueryHelper _requestStatusHelper; + + private static final SettingsManager INSTANCE = new SettingsManager(); + + public static SettingsManager get() + { + return INSTANCE; + } + + private SettingsManager() + { + _requestStatusHelper = new QueryHelper<>(()-> SpecimenSchema.get().getTableInfoSampleRequestStatus(), SpecimenRequestStatus.class); + } + + public RequestNotificationSettings getRequestNotificationSettings(Container container) + { + Map settingsMap = PropertyManager.getProperties(UserManager.getGuestUser(), + container, "SpecimenRequestNotifications"); + if (settingsMap.get("ReplyTo") == null) + { + try (var ignore = SpringActionController.ignoreSqlUpdates()) + { + RequestNotificationSettings defaults = RequestNotificationSettings.getDefaultSettings(container); + saveRequestNotificationSettings(container, defaults); + return defaults; + } + } + else + return new RequestNotificationSettings(settingsMap); + } + + public void saveRequestNotificationSettings(Container container, RequestNotificationSettings settings) + { + PropertyManager.PropertyMap settingsMap = PropertyManager.getWritableProperties(UserManager.getGuestUser(), + container, "SpecimenRequestNotifications", true); + settings.populateMap(settingsMap); + settingsMap.save(); + } + + public void saveDisplaySettings(Container container, DisplaySettings settings) + { + PropertyManager.PropertyMap settingsMap = PropertyManager.getWritableProperties(UserManager.getGuestUser(), + container, "SpecimenRequestDisplay", true); + settings.populateMap(settingsMap); + settingsMap.save(); + } + + public void saveStatusSettings(Container container, StatusSettings settings) + { + PropertyManager.PropertyMap settingsMap = PropertyManager.getWritableProperties(UserManager.getGuestUser(), + container, "SpecimenRequestStatus", true); + settings.populateMap(settingsMap); + settingsMap.save(); + } + + public void saveRepositorySettings(Container container, RepositorySettings settings) + { + PropertyManager.PropertyMap settingsMap = PropertyManager.getWritableProperties(UserManager.getGuestUser(), + container, "SpecimenRepositorySettings", true); + settings.populateMap(settingsMap); + settingsMap.save(); + SpecimenRequestManager.get().clearGroupedValuesForColumn(container); // May have changed groupings + } + + public boolean isSpecimenRequestEnabled(Container container) + { + return isSpecimenRequestEnabled(container, true); + } + + public boolean isSpecimenRequestEnabled(Container container, boolean checkExistingStatuses) + { + if (!checkExistingStatuses) + { + return org.labkey.api.specimen.settings.SettingsManager.get().getRepositorySettings(container).isEnableRequests(); + } + else + { + if (!org.labkey.api.specimen.settings.SettingsManager.get().getRepositorySettings(container).isEnableRequests()) + return false; + List statuses = _requestStatusHelper.getList(container, "SortOrder"); + return (statuses != null && statuses.size() > 1); + } + } +} diff --git a/specimen/src/org/labkey/specimen/view/SpecimenRequestNotificationEmailTemplate.java b/specimen/src/org/labkey/specimen/view/SpecimenRequestNotificationEmailTemplate.java index 1109437a..df3de869 100644 --- a/specimen/src/org/labkey/specimen/view/SpecimenRequestNotificationEmailTemplate.java +++ b/specimen/src/org/labkey/specimen/view/SpecimenRequestNotificationEmailTemplate.java @@ -18,22 +18,18 @@ import org.labkey.api.attachments.Attachment; import org.labkey.api.data.Container; import org.labkey.api.security.User; -import org.labkey.api.specimen.settings.RequestNotificationSettings; -import org.labkey.api.specimen.settings.SettingsManager; import org.labkey.api.study.Study; import org.labkey.api.study.StudyService; import org.labkey.api.util.Link.LinkBuilder; import org.labkey.api.util.PageFlowUtil; import org.labkey.api.util.emailTemplate.EmailTemplate; import org.labkey.specimen.actions.SpecimenController; +import org.labkey.specimen.settings.RequestNotificationSettings; +import org.labkey.specimen.settings.SettingsManager; import java.util.List; import java.util.function.Function; -/** - * User: jeckels - * Date: 11/11/13 - */ public class SpecimenRequestNotificationEmailTemplate extends EmailTemplate { public static final String NAME = "Specimen request notification"; @@ -133,8 +129,7 @@ public void init(NotificationBean notification) _notification = notification; // For backwards compatibility, use the template specified in the notification management UI - RequestNotificationSettings settings = - SettingsManager.get().getRequestNotificationSettings(notification.getContainer()); + RequestNotificationSettings settings = SettingsManager.get().getRequestNotificationSettings(notification.getContainer()); _subjectSuffix = settings.getSubjectSuffix().replaceAll("%requestId%", "" + notification.getRequestId()); } diff --git a/specimen/src/org/labkey/specimen/view/SpecimenToolsWebPartFactory.java b/specimen/src/org/labkey/specimen/view/SpecimenToolsWebPartFactory.java index e71c081d..a49fedcf 100644 --- a/specimen/src/org/labkey/specimen/view/SpecimenToolsWebPartFactory.java +++ b/specimen/src/org/labkey/specimen/view/SpecimenToolsWebPartFactory.java @@ -1,7 +1,6 @@ package org.labkey.specimen.view; import org.labkey.api.specimen.security.permissions.RequestSpecimensPermission; -import org.labkey.api.specimen.settings.SettingsManager; import org.labkey.api.study.StudyUrls; import org.labkey.api.study.security.permissions.ManageStudyPermission; import org.labkey.api.study.view.StudyToolsWebPart; @@ -13,6 +12,7 @@ import org.labkey.specimen.actions.ShowSearchAction; import org.labkey.specimen.actions.SpecimenController.AutoReportListAction; import org.labkey.specimen.actions.SpecimenController.ShowCreateSpecimenRequestAction; +import org.labkey.specimen.settings.SettingsManager; import java.util.ArrayList; import java.util.List; diff --git a/specimen/src/org/labkey/specimen/view/manageNotifications.jsp b/specimen/src/org/labkey/specimen/view/manageNotifications.jsp index 9199bfe9..f1d1aa7a 100644 --- a/specimen/src/org/labkey/specimen/view/manageNotifications.jsp +++ b/specimen/src/org/labkey/specimen/view/manageNotifications.jsp @@ -20,16 +20,15 @@ <%@ page import="org.labkey.api.data.Container" %> <%@ page import="org.labkey.api.security.SecurityUrls" %> <%@ page import="org.labkey.api.security.permissions.AdminPermission" %> -<%@ page import="org.labkey.api.specimen.settings.RequestNotificationSettings" %> -<%@ page import="org.labkey.api.specimen.settings.RequestNotificationSettings.DefaultEmailNotifyEnum" %> -<%@ page import="org.labkey.api.specimen.settings.RequestNotificationSettings.SpecimensAttachmentEnum" %> <%@ page import="org.labkey.api.study.StudyService" %> <%@ 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.HttpView" %> <%@ page import="org.labkey.api.view.JspView" %> <%@ page import="org.labkey.specimen.actions.SpecimenController.ManageNotificationsAction" %> +<%@ page import="org.labkey.specimen.settings.RequestNotificationSettings" %> +<%@ page import="org.labkey.specimen.settings.RequestNotificationSettings.DefaultEmailNotifyEnum" %> +<%@ page import="org.labkey.specimen.settings.RequestNotificationSettings.SpecimensAttachmentEnum" %> <%@ page import="org.labkey.specimen.view.SpecimenRequestNotificationEmailTemplate" %> <%@ page extends="org.labkey.api.jsp.JspBase" %> <%@ taglib prefix="labkey" uri="http://www.labkey.org/taglib" %> diff --git a/specimen/src/org/labkey/specimen/view/webPart.jsp b/specimen/src/org/labkey/specimen/view/webPart.jsp index c55d6878..625b8ede 100644 --- a/specimen/src/org/labkey/specimen/view/webPart.jsp +++ b/specimen/src/org/labkey/specimen/view/webPart.jsp @@ -19,9 +19,8 @@ <%@ page import="org.labkey.api.security.User"%> <%@ page import="org.labkey.api.security.permissions.AdminPermission"%> <%@ page import="org.labkey.api.specimen.security.permissions.RequestSpecimensPermission"%> -<%@ page import="org.labkey.api.specimen.settings.SettingsManager" %> -<%@ page import="org.labkey.specimen.view.SpecimenWebPart" %> <%@ 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.HttpView" %> <%@ page import="org.labkey.specimen.actions.ShowSearchAction" %> @@ -30,7 +29,8 @@ <%@ page import="org.labkey.specimen.actions.SpecimenController.AutoReportListAction" %> <%@ page import="org.labkey.specimen.actions.SpecimenController.ShowCreateSpecimenRequestAction" %> <%@ page import="org.labkey.specimen.actions.SpecimenController.ViewRequestsAction" %> -<%@ page import="org.labkey.api.util.HtmlString" %> +<%@ page import="org.labkey.specimen.settings.SettingsManager" %> +<%@ page import="org.labkey.specimen.view.SpecimenWebPart" %> <%@ page extends="org.labkey.api.jsp.JspBase" %> <% SpecimenWebPart.SpecimenWebPartBean bean = (SpecimenWebPart.SpecimenWebPartBean) HttpView.currentView().getModelBean(); diff --git a/specimen/src/org/labkey/specimen/writer/SpecimenSettingsWriter.java b/specimen/src/org/labkey/specimen/writer/SpecimenSettingsWriter.java index 0c8978b6..8e9707f7 100644 --- a/specimen/src/org/labkey/specimen/writer/SpecimenSettingsWriter.java +++ b/specimen/src/org/labkey/specimen/writer/SpecimenSettingsWriter.java @@ -21,7 +21,6 @@ import org.labkey.api.specimen.importer.RequestabilityManager; import org.labkey.api.specimen.settings.DisplaySettings; import org.labkey.api.specimen.settings.RepositorySettings; -import org.labkey.api.specimen.settings.RequestNotificationSettings; import org.labkey.api.specimen.settings.SettingsManager; import org.labkey.api.specimen.settings.StatusSettings; import org.labkey.api.study.Location; @@ -36,6 +35,7 @@ import org.labkey.specimen.model.SpecimenRequestActor; import org.labkey.specimen.requirements.SpecimenRequestRequirement; import org.labkey.specimen.requirements.SpecimenRequestRequirementProvider; +import org.labkey.specimen.settings.RequestNotificationSettings; import org.labkey.study.xml.DefaultRequirementType; import org.labkey.study.xml.DefaultRequirementsType; import org.labkey.study.xml.SpecimenRepositoryType; @@ -288,7 +288,7 @@ private void writeRequestForm(SpecimenSettingsType specimenSettingsType, SimpleS private void writeNotifications(SpecimenSettingsType specimenSettingsType, SimpleStudyExportContext ctx) { ctx.getLogger().info("Exporting specimen notification settings"); - RequestNotificationSettings notifications = SettingsManager.get().getRequestNotificationSettings(ctx.getContainer()); + RequestNotificationSettings notifications = org.labkey.specimen.settings.SettingsManager.get().getRequestNotificationSettings(ctx.getContainer()); SpecimenSettingsType.Notifications xmlNotifications = specimenSettingsType.addNewNotifications(); if (notifications.getReplyTo() != null) From 01d380047cdc443b0fdd77ad0797c93b02008f29 Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Thu, 23 May 2024 17:04:25 -0700 Subject: [PATCH 416/494] Fix some DbCache-related test failures Original PR: https://github.com/LabKey/platform/pull/5522 --- .../org/labkey/specimen/SpecimenColumns.java | 123 + .../org/labkey/specimen/SpecimenManager.java | 135 +- .../org/labkey/specimen/SpecimenModule.java | 67 +- .../specimen/SpecimenRequestException.java | 13 + .../specimen/SpecimenRequestManager.java | 50 +- .../specimen/SpecimenRequestStatus.java | 126 + .../labkey/specimen/SpecimenServiceImpl.java | 10 +- .../labkey/specimen/SpecimenTableManager.java | 198 ++ .../specimen/actions/ManageRequestBean.java | 2 +- .../actions/ParticipantCommentAction.java | 3 +- .../actions/ShowUploadSpecimensAction.java | 9 +- .../actions/SpecimenApiController.java | 8 +- .../specimen/actions/SpecimenController.java | 49 +- .../specimen/actions/SpecimenHeaderBean.java | 2 +- .../specimen/actions/SpecimenSearchBean.java | 12 +- .../actions/SpecimenViewTypeForm.java | 2 +- .../specimen/actions/SpecimensViewBean.java | 2 +- .../actions/ViewRequestsHeaderBean.java | 2 +- .../importer/AbstractSpecimenTask.java | 1 - .../DefaultSpecimenImportStrategyFactory.java | 1 - .../importer/EditableSpecimenImporter.java | 274 ++ .../FileSystemSpecimenImportFile.java | 81 + .../labkey/specimen/importer/ImportTypes.java | 17 + .../specimen/importer/ImportableColumn.java | 174 + .../importer/IteratorSpecimenImportFile.java | 61 + .../importer/RequestabilityManager.java | 856 +++++ .../importer/SimpleSpecimenImporter.java | 445 +++ .../specimen/importer/SpecimenColumn.java | 110 + .../specimen/importer/SpecimenImportFile.java | 34 + .../specimen/importer/SpecimenImporter.java | 3127 +++++++++++++++++ .../importer/SpecimenSettingsImporter.java | 3 +- .../specimen/importer/SpecimenTableType.java | 32 + .../StandardSpecimenImportStrategy.java | 60 + .../labkey/specimen/importer/TargetTable.java | 158 + .../DefaultRequestNotification.java | 2 +- .../specimen/query/BaseSpecimenQueryView.java | 97 + .../query/SpecimenEventQueryView.java | 1 - .../specimen/query/SpecimenQueryView.java | 1044 ++++++ .../query/SpecimenRequestDisplayColumn.java | 128 + .../query/SpecimenRequestQueryView.java | 3 +- .../specimen/query/SpecimenUpdateService.java | 541 +++ .../specimen/report/SpecimenVisitReport.java | 2 +- .../RequestEnrollmentLocationReport.java | 2 +- .../RequestEnrollmentSiteReportFactory.java | 2 +- .../report/request/RequestLocationReport.java | 2 +- .../request/RequestParticipantReport.java | 2 +- .../report/request/RequestReport.java | 2 +- .../specimen/settings/SettingsManager.java | 2 +- .../specimen/view/NotificationBean.java | 2 +- .../view/configRequestabilityRules.jsp | 4 +- .../labkey/specimen/view/manageRequest.jsp | 8 +- .../specimen/view/manageRequestStatus.jsp | 4 +- .../specimen/view/manageStatusOrder.jsp | 2 +- .../labkey/specimen/view/manageStatuses.jsp | 4 +- .../specimen/view/viewRequestsHeader.jsp | 2 +- .../writer/LocationSpecimenWriter.java | 2 +- .../writer/SpecimenArchiveWriter.java | 2 +- .../writer/SpecimenSettingsWriter.java | 4 +- .../specimen/writer/SpecimenWriter.java | 6 +- .../writer/StandardSpecimenWriter.java | 2 +- 60 files changed, 7949 insertions(+), 170 deletions(-) create mode 100644 specimen/src/org/labkey/specimen/SpecimenColumns.java create mode 100644 specimen/src/org/labkey/specimen/SpecimenRequestException.java create mode 100644 specimen/src/org/labkey/specimen/SpecimenRequestStatus.java create mode 100644 specimen/src/org/labkey/specimen/SpecimenTableManager.java create mode 100644 specimen/src/org/labkey/specimen/importer/EditableSpecimenImporter.java create mode 100644 specimen/src/org/labkey/specimen/importer/FileSystemSpecimenImportFile.java create mode 100644 specimen/src/org/labkey/specimen/importer/ImportTypes.java create mode 100644 specimen/src/org/labkey/specimen/importer/ImportableColumn.java create mode 100644 specimen/src/org/labkey/specimen/importer/IteratorSpecimenImportFile.java create mode 100644 specimen/src/org/labkey/specimen/importer/RequestabilityManager.java create mode 100644 specimen/src/org/labkey/specimen/importer/SimpleSpecimenImporter.java create mode 100644 specimen/src/org/labkey/specimen/importer/SpecimenColumn.java create mode 100644 specimen/src/org/labkey/specimen/importer/SpecimenImportFile.java create mode 100644 specimen/src/org/labkey/specimen/importer/SpecimenImporter.java create mode 100644 specimen/src/org/labkey/specimen/importer/SpecimenTableType.java create mode 100644 specimen/src/org/labkey/specimen/importer/StandardSpecimenImportStrategy.java create mode 100644 specimen/src/org/labkey/specimen/importer/TargetTable.java create mode 100644 specimen/src/org/labkey/specimen/query/BaseSpecimenQueryView.java create mode 100644 specimen/src/org/labkey/specimen/query/SpecimenQueryView.java create mode 100644 specimen/src/org/labkey/specimen/query/SpecimenRequestDisplayColumn.java create mode 100644 specimen/src/org/labkey/specimen/query/SpecimenUpdateService.java diff --git a/specimen/src/org/labkey/specimen/SpecimenColumns.java b/specimen/src/org/labkey/specimen/SpecimenColumns.java new file mode 100644 index 00000000..613b660f --- /dev/null +++ b/specimen/src/org/labkey/specimen/SpecimenColumns.java @@ -0,0 +1,123 @@ +package org.labkey.specimen; + +import org.labkey.specimen.importer.ImportTypes; +import org.labkey.specimen.importer.ImportableColumn; +import org.labkey.specimen.importer.SpecimenColumn; +import org.labkey.specimen.importer.TargetTable; + +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +// Column definitions that are shared between specimen writers and importers +public class SpecimenColumns +{ + public static final String GLOBAL_UNIQUE_ID_TSV_COL = "global_unique_specimen_id"; + + public static final String LAB_ID_TSV_COL = "lab_id"; + public static final String SPEC_NUMBER_TSV_COL = "specimen_number"; + public static final String EVENT_ID_COL = "record_id"; + public static final String VISIT_COL = "visit_value"; + + // SpecimenEvent columns that form a psuedo-unique constraint + public static final SpecimenColumn GLOBAL_UNIQUE_ID, LAB_ID, SHIP_DATE, STORAGE_DATE, LAB_RECEIPT_DATE, DRAW_TIMESTAMP; + public static final SpecimenColumn VISIT_VALUE; + + public static final List BASE_SPECIMEN_COLUMNS = List.of( + new SpecimenColumn(EVENT_ID_COL, "ExternalId", "BIGINT NOT NULL", TargetTable.SPECIMEN_EVENTS, true), + new SpecimenColumn("record_source", "RecordSource", "VARCHAR(20)", TargetTable.SPECIMEN_EVENTS), + GLOBAL_UNIQUE_ID = new SpecimenColumn(GLOBAL_UNIQUE_ID_TSV_COL, "GlobalUniqueId", "VARCHAR(50)", true, TargetTable.VIALS, true), + LAB_ID = new SpecimenColumn(LAB_ID_TSV_COL, "LabId", "INT", TargetTable.SPECIMEN_EVENTS, "Site", "ExternalId", "LEFT OUTER") { + @Override + public boolean isUnique() { return true; } + }, + new SpecimenColumn("originating_location", "OriginatingLocationId", "INT", TargetTable.SPECIMENS_AND_SPECIMEN_EVENTS, "Site", "ExternalId", "LEFT OUTER"), + new SpecimenColumn("unique_specimen_id", "UniqueSpecimenId", "VARCHAR(50)", TargetTable.SPECIMEN_EVENTS), + new SpecimenColumn("ptid", "Ptid", "VARCHAR(32)", true, TargetTable.SPECIMENS_AND_SPECIMEN_EVENTS), + new SpecimenColumn("parent_specimen_id", "ParentSpecimenId", "INT", TargetTable.SPECIMEN_EVENTS), + DRAW_TIMESTAMP = new SpecimenColumn("draw_timestamp", "DrawTimestamp", ImportTypes.DATETIME_TYPE, TargetTable.SPECIMENS_AND_SPECIMEN_EVENTS), + new SpecimenColumn("sal_receipt_date", "SalReceiptDate", ImportTypes.DATETIME_TYPE, TargetTable.SPECIMENS_AND_SPECIMEN_EVENTS), + new SpecimenColumn(SPEC_NUMBER_TSV_COL, "SpecimenNumber", "VARCHAR(50)", true, TargetTable.SPECIMEN_EVENTS), + new SpecimenColumn("class_id", "ClassId", "VARCHAR(20)", TargetTable.SPECIMENS_AND_SPECIMEN_EVENTS), + VISIT_VALUE = new SpecimenColumn(VISIT_COL, "VisitValue", ImportTypes.NUMERIC_TYPE, TargetTable.SPECIMENS_AND_SPECIMEN_EVENTS), + new SpecimenColumn("protocol_number", "ProtocolNumber", "VARCHAR(20)", TargetTable.SPECIMENS_AND_SPECIMEN_EVENTS), + new SpecimenColumn("visit_description", "VisitDescription", "VARCHAR(10)", TargetTable.SPECIMENS_AND_SPECIMEN_EVENTS), + new SpecimenColumn("other_specimen_id", "OtherSpecimenId", "VARCHAR(50)", TargetTable.SPECIMEN_EVENTS), + new SpecimenColumn("volume", "Volume", "FLOAT", TargetTable.VIALS_AND_SPECIMEN_EVENTS, "MAX"), + new SpecimenColumn("volume_units", "VolumeUnits", "VARCHAR(20)", TargetTable.SPECIMENS_AND_SPECIMEN_EVENTS), + new SpecimenColumn("stored", "Stored", "INT", TargetTable.SPECIMEN_EVENTS), + new SpecimenColumn("storage_flag", "storageFlag", "INT", TargetTable.SPECIMEN_EVENTS), + STORAGE_DATE = new SpecimenColumn("storage_date", "StorageDate", ImportTypes.DATETIME_TYPE, TargetTable.SPECIMEN_EVENTS, true), + new SpecimenColumn("ship_flag", "ShipFlag", "INT", TargetTable.SPECIMEN_EVENTS), + new SpecimenColumn("ship_batch_number", "ShipBatchNumber", "INT", TargetTable.SPECIMEN_EVENTS), + SHIP_DATE = new SpecimenColumn("ship_date", "ShipDate", ImportTypes.DATETIME_TYPE, TargetTable.SPECIMEN_EVENTS, true), + new SpecimenColumn("imported_batch_number", "ImportedBatchNumber", "INT", TargetTable.SPECIMEN_EVENTS), + LAB_RECEIPT_DATE = new SpecimenColumn("lab_receipt_date", "LabReceiptDate", ImportTypes.DATETIME_TYPE, TargetTable.SPECIMEN_EVENTS, true), + new SpecimenColumn("expected_time_value", "ExpectedTimeValue", "FLOAT", TargetTable.SPECIMEN_EVENTS), + new SpecimenColumn("expected_time_unit", "ExpectedTimeUnit", "VARCHAR(15)", TargetTable.SPECIMEN_EVENTS), + new SpecimenColumn("group_protocol", "GroupProtocol", "INT", TargetTable.SPECIMEN_EVENTS), + new SpecimenColumn("sub_additive_derivative", "SubAdditiveDerivative", "VARCHAR(50)", TargetTable.SPECIMENS_AND_SPECIMEN_EVENTS), + new SpecimenColumn("comments", "Comments", "VARCHAR(500)", TargetTable.SPECIMEN_EVENTS), + new SpecimenColumn("primary_specimen_type_id", "PrimaryTypeId", "INT", TargetTable.SPECIMENS_AND_SPECIMEN_EVENTS, "SpecimenPrimaryType", "ExternalId", "LEFT OUTER"), + new SpecimenColumn("derivative_type_id", "DerivativeTypeId", "INT", TargetTable.SPECIMENS_AND_SPECIMEN_EVENTS, "SpecimenDerivative", "ExternalId", "LEFT OUTER"), + new SpecimenColumn("derivative_type_id_2", "DerivativeTypeId2", "INT", TargetTable.SPECIMENS_AND_SPECIMEN_EVENTS, "SpecimenDerivative", "ExternalId", "LEFT OUTER"), + new SpecimenColumn("additive_type_id", "AdditiveTypeId", "INT", TargetTable.SPECIMENS_AND_SPECIMEN_EVENTS, "SpecimenAdditive", "ExternalId", "LEFT OUTER"), + new SpecimenColumn("specimen_condition", "SpecimenCondition", "VARCHAR(30)", TargetTable.SPECIMEN_EVENTS), + new SpecimenColumn("sample_number", "SampleNumber", "INT", TargetTable.SPECIMEN_EVENTS), + new SpecimenColumn("x_sample_origin", "XSampleOrigin", "VARCHAR(50)", TargetTable.SPECIMEN_EVENTS), + new SpecimenColumn("external_location", "ExternalLocation", "VARCHAR(50)", TargetTable.SPECIMEN_EVENTS), + new SpecimenColumn("update_timestamp", "UpdateTimestamp", ImportTypes.DATETIME_TYPE, TargetTable.SPECIMEN_EVENTS), + new SpecimenColumn("requestable", "Requestable", ImportTypes.BOOLEAN_TYPE, TargetTable.VIALS), + new SpecimenColumn("shipped_from_lab", "ShippedFromLab", "VARCHAR(32)", TargetTable.SPECIMEN_EVENTS), + new SpecimenColumn("shipped_to_lab", "ShippedtoLab", "VARCHAR(32)", TargetTable.SPECIMEN_EVENTS), + new SpecimenColumn("frozen_time", "FrozenTime", ImportTypes.DURATION_TYPE, TargetTable.SPECIMEN_EVENTS), + new SpecimenColumn("primary_volume", "PrimaryVolume", "FLOAT", TargetTable.VIALS_AND_SPECIMEN_EVENTS), + new SpecimenColumn("primary_volume_units", "PrimaryVolumeUnits", "VARCHAR(20)", TargetTable.VIALS_AND_SPECIMEN_EVENTS), + new SpecimenColumn("processed_by_initials", "ProcessedByInitials", "VARCHAR(32)", TargetTable.SPECIMEN_EVENTS), + new SpecimenColumn("processing_date", "ProcessingDate", ImportTypes.DATETIME_TYPE, TargetTable.SPECIMEN_EVENTS), + new SpecimenColumn("processing_time", "ProcessingTime", ImportTypes.DURATION_TYPE, TargetTable.SPECIMEN_EVENTS), + new SpecimenColumn("quality_comments", "QualityComments", "VARCHAR(500)", TargetTable.SPECIMEN_EVENTS), + new SpecimenColumn("total_cell_count", "TotalCellCount", "FLOAT", TargetTable.VIALS_AND_SPECIMEN_EVENTS), + new SpecimenColumn("tube_type", "TubeType", "VARCHAR(64)", TargetTable.VIALS_AND_SPECIMEN_EVENTS), + new SpecimenColumn("input_hash", "InputHash", ImportTypes.BINARY_TYPE, TargetTable.SPECIMEN_EVENTS) // Not pulled from file... maybe this should be a ComputedColumn + ); + + public static final Collection ADDITIVE_COLUMNS = List.of( + new ImportableColumn("additive_id", "ExternalId", "INT NOT NULL", true), + new ImportableColumn("ldms_additive_code", "LdmsAdditiveCode", "VARCHAR(30)"), + new ImportableColumn("labware_additive_code", "LabwareAdditiveCode", "VARCHAR(20)"), + new ImportableColumn("additive", "Additive", "VARCHAR(100)") + ); + + public static final Collection DERIVATIVE_COLUMNS = Arrays.asList( + new ImportableColumn("derivative_id", "ExternalId", "INT NOT NULL", true), + new ImportableColumn("ldms_derivative_code", "LdmsDerivativeCode", "VARCHAR(30)"), + new ImportableColumn("labware_derivative_code", "LabwareDerivativeCode", "VARCHAR(20)"), + new ImportableColumn("derivative", "Derivative", "VARCHAR(100)") + ); + + public static final Collection SITE_COLUMNS = List.of( + new ImportableColumn("lab_id", "ExternalId", "INT NOT NULL", true), + new ImportableColumn("ldms_lab_code", "LdmsLabCode", "INT"), + new ImportableColumn("labware_lab_code", "LabwareLabCode", "VARCHAR(20)", false, true), + new ImportableColumn("lab_name", "Label", "VARCHAR(200)", false, true), + new ImportableColumn("lab_upload_code", "LabUploadCode", "VARCHAR(10)"), + new ImportableColumn("is_sal", "Sal", ImportTypes.BOOLEAN_TYPE, Boolean.FALSE), + new ImportableColumn("is_repository", "Repository", ImportTypes.BOOLEAN_TYPE, Boolean.FALSE), + new ImportableColumn("is_clinic", "Clinic", ImportTypes.BOOLEAN_TYPE, Boolean.FALSE), + new ImportableColumn("is_endpoint", "Endpoint", ImportTypes.BOOLEAN_TYPE, Boolean.FALSE), + new ImportableColumn("street_address", "StreetAddress", "VARCHAR(200)", false, true), + new ImportableColumn("city", "City", "VARCHAR(200)", false, true), + new ImportableColumn("governing_district", "GoverningDistrict", "VARCHAR(200)", false, true), + new ImportableColumn("country", "Country", "VARCHAR(200)", false, true), + new ImportableColumn("postal_area", "PostalArea", "VARCHAR(50)", false, true), + new ImportableColumn("description", "Description", "VARCHAR(500)", false, true) + ); + + public static final Collection PRIMARYTYPE_COLUMNS = List.of( + new ImportableColumn("primary_type_id", "ExternalId", "INT NOT NULL", true), + new ImportableColumn("primary_type_ldms_code", "PrimaryTypeLdmsCode", "VARCHAR(5)"), + new ImportableColumn("primary_type_labware_code", "PrimaryTypeLabwareCode", "VARCHAR(5)"), + new ImportableColumn("primary_type", "PrimaryType", "VARCHAR(100)") + ); +} diff --git a/specimen/src/org/labkey/specimen/SpecimenManager.java b/specimen/src/org/labkey/specimen/SpecimenManager.java index 8165c3b5..749ab621 100644 --- a/specimen/src/org/labkey/specimen/SpecimenManager.java +++ b/specimen/src/org/labkey/specimen/SpecimenManager.java @@ -1,5 +1,6 @@ package org.labkey.specimen; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.labkey.api.audit.AuditLogService; import org.labkey.api.data.ColumnInfo; @@ -8,6 +9,7 @@ import org.labkey.api.data.SQLFragment; import org.labkey.api.data.SimpleFilter; import org.labkey.api.data.Sort; +import org.labkey.api.data.SqlExecutor; import org.labkey.api.data.SqlSelector; import org.labkey.api.data.Table; import org.labkey.api.data.TableInfo; @@ -18,9 +20,10 @@ import org.labkey.api.query.QueryService; import org.labkey.api.query.UserSchema; import org.labkey.api.security.User; +import org.labkey.api.specimen.SpecimenEvent; +import org.labkey.api.specimen.SpecimenEventManager; import org.labkey.api.specimen.SpecimenManagerNew; import org.labkey.api.specimen.SpecimenQuerySchema; -import org.labkey.api.specimen.SpecimenRequestException; import org.labkey.api.specimen.SpecimenSchema; import org.labkey.api.specimen.Vial; import org.labkey.api.specimen.location.LocationImpl; @@ -34,6 +37,7 @@ import org.labkey.api.util.DateUtil; import org.labkey.api.util.HtmlString; import org.labkey.api.util.Path; +import org.labkey.api.view.NotFoundException; import org.labkey.api.view.ViewContext; import org.labkey.specimen.model.AdditiveType; import org.labkey.specimen.model.DerivativeType; @@ -347,7 +351,7 @@ public List getVials(Container container, User user, String participantId, SimpleFilter filter = SimpleFilter.createContainerFilter(container); filter.addClause(new SimpleFilter.SQLClause("LOWER(ptid) = LOWER(?)", new Object[] {participantId}, FieldKey.fromParts("ptid"))); filter.addCondition(FieldKey.fromParts("VisitValue"), visit); - return SpecimenManagerNew.get().getVials(container, user, filter); + return getVials(container, user, filter); } public List getVials(Container container, User user, int[] vialsRowIds) @@ -355,7 +359,7 @@ public List getVials(Container container, User user, int[] vialsRowIds) Set uniqueRowIds = new HashSet<>(vialsRowIds.length); for (int vialRowId : vialsRowIds) uniqueRowIds.add((long)vialRowId); - return SpecimenManagerNew.get().getVials(container, user, uniqueRowIds); + return getVials(container, user, uniqueRowIds); } public List getVials(Container container, User user, String[] globalUniqueIds) throws SpecimenRequestException @@ -365,7 +369,7 @@ public List getVials(Container container, User user, String[] globalUnique Collections.addAll(uniqueRowIds, globalUniqueIds); List ids = new ArrayList<>(uniqueRowIds); filter.addInClause(FieldKey.fromParts("GlobalUniqueId"), ids); - List vials = SpecimenManagerNew.get().getVials(container, user, filter); + List vials = getVials(container, user, filter); if (vials == null || vials.size() != ids.size()) throw new SpecimenRequestException("Vial not found."); // an id has no matching specimen, let caller determine what to report return vials; @@ -377,7 +381,7 @@ public List getVials(Container container, User user, String participantId, Calendar endCal = DateUtil.newCalendar(date.getTime()); endCal.add(Calendar.DATE, 1); filter.addClause(new SimpleFilter.SQLClause("DrawTimestamp >= ? AND DrawTimestamp < ?", new Object[] {date, endCal.getTime()})); - return SpecimenManagerNew.get().getVials(container, user, filter); + return getVials(container, user, filter); } @Nullable @@ -415,14 +419,14 @@ private List getDerivativeTypes(final Container container, @Null public boolean isSpecimensEmpty(Container container, User user) { - TableSelector selector = SpecimenManagerNew.get().getSpecimensSelector(container, user, null); + TableSelector selector = getSpecimensSelector(container, user, null); return !selector.exists(); } public Vial getVial(Container container, User user, String globalUniqueId) { SimpleFilter filter = new SimpleFilter(new SimpleFilter.SQLClause("LOWER(GlobalUniqueId) = LOWER(?)", new Object[] { globalUniqueId })); - List matches = SpecimenManagerNew.get().getVials(container, user, filter); + List matches = getVials(container, user, filter); if (matches == null || matches.isEmpty()) return null; if (matches.size() > 1) @@ -453,7 +457,7 @@ public List getRequestableVials(Container container, User user, Set { SimpleFilter filter = SimpleFilter.createContainerFilter(container); filter.addInClause(FieldKey.fromParts("RowId"), vialRowIds).addCondition(FieldKey.fromString("available"), true); - return SpecimenManagerNew.get().getVials(container, user, filter); + return getVials(container, user, filter); } public Map> getVialsForSpecimenHashes(Container container, User user, Collection hashes, boolean onlyAvailable) @@ -462,7 +466,7 @@ public Map> getVialsForSpecimenHashes(Container container, Use filter.addInClause(FieldKey.fromParts("SpecimenHash"), hashes); if (onlyAvailable) filter.addCondition(FieldKey.fromParts("Available"), true); - List vials = SpecimenManagerNew.get().getVials(container, user, filter); + List vials = getVials(container, user, filter); Map> map = new HashMap<>(); for (Vial vial : vials) { @@ -473,4 +477,117 @@ public Map> getVialsForSpecimenHashes(Container container, Use return map; } + + public List getVials(Container container, User user, Set vialRowIds) + { + // Take a set to eliminate dups - issue 26940 + + SimpleFilter filter = SimpleFilter.createContainerFilter(container); + filter.addInClause(FieldKey.fromParts("RowId"), vialRowIds); + List vials = getVials(container, user, filter); + if (vials.size() != vialRowIds.size()) + { + List unmatchedRowIds = new ArrayList<>(vialRowIds); + for (Vial vial : vials) + { + unmatchedRowIds.remove(vial.getRowId()); + } + throw new SpecimenRequestException("One or more specimen RowIds had no matching specimen: " + unmatchedRowIds); + } + return vials; + } + + public List getVials(final Container container, final User user, SimpleFilter filter) + { + // TODO: LinkedList? + final List vials = new ArrayList<>(); + + getSpecimensSelector(container, user, filter) + .forEachMap(map -> vials.add(new Vial(container, map))); + + return vials; + } + + public TableSelector getSpecimensSelector(final Container container, final User user, SimpleFilter filter) + { + Study study = StudyService.get().getStudy(container); + if (study == null) + { + throw new NotFoundException("No study in container " + container.getPath()); + } + UserSchema schema = SpecimenQuerySchema.get(study, user); + TableInfo specimenTable = schema.getTable(SpecimenQuerySchema.SPECIMEN_WRAP_TABLE_NAME); + return new TableSelector(specimenTable, filter, null); + } + + public Vial getVial(Container container, User user, long rowId) + { + SimpleFilter filter = new SimpleFilter(FieldKey.fromParts("RowId"), rowId); + List vials = getVials(container, user, filter); + if (vials.isEmpty()) + return null; + return vials.get(0); + } + + public void deleteSpecimen(@NotNull Vial vial, boolean clearCaches) + { + Container container = vial.getContainer(); + TableInfo tableInfoSpecimenEvent = SpecimenSchema.get().getTableInfoSpecimenEvent(container); + TableInfo tableInfoVial = SpecimenSchema.get().getTableInfoVial(container); + if (null == tableInfoSpecimenEvent || null == tableInfoVial) + return; + + String tableInfoSpecimenEventSelectName = tableInfoSpecimenEvent.getSelectName(); + String tableInfoVialSelectName = tableInfoVial.getSelectName(); + + SQLFragment sqlFragmentEvent = new SQLFragment("DELETE FROM "); + sqlFragmentEvent.append(tableInfoSpecimenEventSelectName).append(" WHERE VialId = ?"); + sqlFragmentEvent.add(vial.getRowId()); + new SqlExecutor(SpecimenSchema.get().getSchema()).execute(sqlFragmentEvent); + + SQLFragment sqlFragment = new SQLFragment("DELETE FROM "); + sqlFragment.append(tableInfoVialSelectName).append(" WHERE RowId = ?"); + sqlFragment.add(vial.getRowId()); + new SqlExecutor(SpecimenSchema.get().getSchema()).execute(sqlFragment); + + if (clearCaches) + { + SpecimenRequestManager.get().clearCaches(vial.getContainer()); + } + } + + public long getMaxExternalId(Container container) + { + TableInfo tableInfo = SpecimenSchema.get().getTableInfoSpecimenEvent(container); + SQLFragment sql = new SQLFragment("SELECT MAX(ExternalId) FROM "); + sql.append(tableInfo); + return new SqlSelector(tableInfo.getSchema(), sql).getArrayList(Long.class).get(0); + } + + public String getFirstProcessedByInitials(List dateOrderedEvents) + { + SpecimenEvent firstEvent = SpecimenEventManager.get().getFirstEvent(dateOrderedEvents); + return firstEvent != null ? firstEvent.getProcessedByInitials() : null; + } + + public List getSpecimenEvents(List vials, boolean includeObsolete) + { + if (vials == null || vials.isEmpty()) + return Collections.emptyList(); + Collection vialIds = new HashSet<>(); + Container container = null; + for (Vial vial : vials) + { + vialIds.add(vial.getRowId()); + if (container == null) + container = vial.getContainer(); + else if (!container.equals(vial.getContainer())) + throw new IllegalArgumentException("All specimens must be from the same container"); + } + SimpleFilter filter = new SimpleFilter(); + filter.addInClause(FieldKey.fromString("VialId"), vialIds); + if (!includeObsolete) + filter.addCondition(FieldKey.fromString("Obsolete"), false); + return SpecimenEventManager.get().getSpecimenEvents(container, filter); + } } diff --git a/specimen/src/org/labkey/specimen/SpecimenModule.java b/specimen/src/org/labkey/specimen/SpecimenModule.java index 01c6f48f..e74bb9f6 100644 --- a/specimen/src/org/labkey/specimen/SpecimenModule.java +++ b/specimen/src/org/labkey/specimen/SpecimenModule.java @@ -16,6 +16,7 @@ package org.labkey.specimen; +import jakarta.servlet.http.HttpServletResponse; import org.apache.commons.collections4.bag.HashBag; import org.apache.commons.lang3.mutable.MutableInt; import org.apache.logging.log4j.Logger; @@ -36,21 +37,24 @@ import org.labkey.api.pipeline.PipelineJobException; import org.labkey.api.pipeline.PipelineService; import org.labkey.api.query.FieldKey; -import org.labkey.api.query.QueryParam; +import org.labkey.api.query.QuerySettings; +import org.labkey.api.query.QueryUpdateService; import org.labkey.api.query.QueryView; import org.labkey.api.query.ValidationException; +import org.labkey.api.reader.TabLoader; import org.labkey.api.security.User; import org.labkey.api.security.roles.RoleManager; import org.labkey.api.specimen.SpecimenMigrationService; import org.labkey.api.specimen.SpecimenQuerySchema; import org.labkey.api.specimen.SpecimenSchema; import org.labkey.api.specimen.SpecimensPage; -import org.labkey.api.specimen.importer.SpecimenImporter; import org.labkey.api.specimen.settings.RepositorySettings; import org.labkey.api.specimen.settings.SettingsManager; +import org.labkey.api.study.MapArrayExcelWriter; import org.labkey.api.study.SpecimenService; import org.labkey.api.study.StudyInternalService; import org.labkey.api.study.StudyService; +import org.labkey.api.study.TimepointType; import org.labkey.api.study.importer.SimpleStudyImportContext; import org.labkey.api.study.importer.SimpleStudyImporterRegistry; import org.labkey.api.study.writer.SimpleStudyWriterRegistry; @@ -59,17 +63,23 @@ import org.labkey.api.util.logging.LogHelper; import org.labkey.api.view.ActionURL; import org.labkey.api.view.HttpView; +import org.labkey.api.view.ViewContext; import org.labkey.api.view.WebPartFactory; import org.labkey.specimen.actions.SpecimenApiController; import org.labkey.specimen.actions.SpecimenController; import org.labkey.specimen.importer.AbstractSpecimenTask; import org.labkey.specimen.importer.DefaultSpecimenImportStrategyFactory; +import org.labkey.specimen.importer.RequestabilityManager; +import org.labkey.specimen.importer.SimpleSpecimenImporter; +import org.labkey.specimen.importer.SpecimenImporter; import org.labkey.specimen.importer.SpecimenSchemaImporter; import org.labkey.specimen.importer.SpecimenSettingsImporter; import org.labkey.specimen.model.SpecimenRequestEventType; import org.labkey.specimen.pipeline.SampleMindedTransform; import org.labkey.specimen.pipeline.SampleMindedTransformTask; import org.labkey.specimen.pipeline.SpecimenPipeline; +import org.labkey.specimen.query.SpecimenQueryView; +import org.labkey.specimen.query.SpecimenUpdateService; import org.labkey.specimen.security.roles.SpecimenCoordinatorRole; import org.labkey.specimen.security.roles.SpecimenRequesterRole; import org.labkey.specimen.view.ManageSpecimenView; @@ -82,6 +92,7 @@ import org.labkey.specimen.writer.SpecimenSettingsWriter; import org.labkey.specimen.writer.SpecimenWriter; +import java.io.IOException; import java.nio.file.Path; import java.util.Collection; import java.util.Collections; @@ -163,47 +174,63 @@ public ActionURL getSpecimensURL(Container c) } @Override - public ActionURL getSpecimenEventsURL(Container c, ActionURL returnURL) + public void importSpecimenArchive(@Nullable Path inputFile, PipelineJob job, SimpleStudyImportContext ctx, boolean merge, boolean syncParticipantVisit) throws PipelineJobException, ValidationException { - return new ActionURL(SpecimenController.SpecimenEventsAction.class, c).addReturnURL(returnURL); + AbstractSpecimenTask.doImport(inputFile, job, ctx, merge, syncParticipantVisit); } @Override - public ActionURL getInsertSpecimenQueryRowURL(Container c, String schemaName, TableInfo table) + public void clearRequestCaches(Container c) { - ActionURL url = new ActionURL(SpecimenController.InsertSpecimenQueryRowAction.class, c); - url.addParameter("schemaName", schemaName); - url.addParameter(QueryView.DATAREGIONNAME_DEFAULT + "." + QueryParam.queryName, table.getName()); + SpecimenRequestManager.get().clearCaches(c); + } + + @Override + public @Nullable QueryUpdateService getSpecimenQueryUpdateService(Container c, TableInfo queryTable) + { + return SettingsManager.get().getRepositorySettings(c).isSpecimenDataEditable() ? new SpecimenUpdateService(queryTable) : null; + } - return url; + @Override + public void importSpecimens(Container container, User user, List> specimens) throws ValidationException, IOException + { + SimpleSpecimenImporter importer = new SimpleSpecimenImporter(container, user); + importer.process(specimens, false); } @Override - public ActionURL getUpdateSpecimenQueryRowURL(Container c, String schemaName, TableInfo table) + public void exportSpecimens(Container container, User user, List> specimens, TimepointType timepointType, String participantIdLabel, HttpServletResponse response) { - ActionURL url = new ActionURL(SpecimenController.UpdateSpecimenQueryRowAction.class, c); - url.addParameter("schemaName", schemaName); - url.addParameter(QueryView.DATAREGIONNAME_DEFAULT + "." + QueryParam.queryName, table.getName()); + SimpleSpecimenImporter importer = new SimpleSpecimenImporter(container, user, timepointType, participantIdLabel); + MapArrayExcelWriter xlWriter = new MapArrayExcelWriter(specimens, importer.getSimpleSpecimenColumns()); + // Note: I don't think this is having any effect on the output because ExcelColumn.renderCaption() uses + // the DisplayColumn's caption, not its own caption. That seems wrong... + xlWriter.setColumnModifier(col -> col.setCaption(importer.label(col.getName()))); + xlWriter.renderWorkbook(response); + } - return url; + @Override + public Map getColumnLabelMap(Container container, User user) + { + return new SimpleSpecimenImporter(container, user).getColumnLabels(); } @Override - public void importSpecimenArchive(@Nullable Path inputFile, PipelineJob job, SimpleStudyImportContext ctx, boolean merge, boolean syncParticipantVisit) throws PipelineJobException, ValidationException + public void fixupSpecimenColumns(Container container, User user, TabLoader loader) throws IOException { - AbstractSpecimenTask.doImport(inputFile, job, ctx, merge, syncParticipantVisit); + new SimpleSpecimenImporter(container, user).fixupSpecimenColumns(loader); } @Override - public void clearRequestCaches(Container c) + public QueryView getSpecimenQueryView(ViewContext context, QuerySettings settings) { - SpecimenRequestManager.get().clearCaches(c); + return SpecimenQueryView.createView(context, settings, SpecimenQueryView.ViewType.VIALS); } @Override - public void updateVialCounts(Container container, User user) + public void setDefaultRequestabilityRules(Container container, User user) { - SpecimenRequestManager.get().updateVialCounts(container, user); + RequestabilityManager.getInstance().setDefaultRules(container, user); } }); } diff --git a/specimen/src/org/labkey/specimen/SpecimenRequestException.java b/specimen/src/org/labkey/specimen/SpecimenRequestException.java new file mode 100644 index 00000000..9f1e6831 --- /dev/null +++ b/specimen/src/org/labkey/specimen/SpecimenRequestException.java @@ -0,0 +1,13 @@ +package org.labkey.specimen; + +public class SpecimenRequestException extends RuntimeException +{ + public SpecimenRequestException() + { + } + + public SpecimenRequestException(String message) + { + super(message); + } +} diff --git a/specimen/src/org/labkey/specimen/SpecimenRequestManager.java b/specimen/src/org/labkey/specimen/SpecimenRequestManager.java index 2bcfff0f..000f2d1b 100644 --- a/specimen/src/org/labkey/specimen/SpecimenRequestManager.java +++ b/specimen/src/org/labkey/specimen/SpecimenRequestManager.java @@ -33,13 +33,9 @@ import org.labkey.api.query.ValidationException; import org.labkey.api.security.User; import org.labkey.api.settings.AppProps; -import org.labkey.api.specimen.SpecimenManagerNew; import org.labkey.api.specimen.SpecimenQuerySchema; -import org.labkey.api.specimen.SpecimenRequestException; -import org.labkey.api.specimen.SpecimenRequestStatus; import org.labkey.api.specimen.SpecimenSchema; import org.labkey.api.specimen.Vial; -import org.labkey.api.specimen.importer.RequestabilityManager; import org.labkey.api.specimen.importer.RollupHelper; import org.labkey.api.specimen.importer.RollupInstance; import org.labkey.api.specimen.importer.VialSpecimenRollup; @@ -55,6 +51,7 @@ import org.labkey.api.util.Pair; import org.labkey.api.util.ReentrantLockWithName; import org.labkey.api.view.ActionURL; +import org.labkey.specimen.importer.RequestabilityManager; import org.labkey.specimen.model.SpecimenRequestEvent; import org.labkey.specimen.requirements.SpecimenRequest; import org.labkey.specimen.requirements.SpecimenRequestRequirement; @@ -73,6 +70,7 @@ import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.concurrent.locks.ReentrantLock; @@ -167,11 +165,6 @@ public Set getRequestStatusIdsInUse(Container c) return uniqueStatuses; } - public List getRequestEvents(Container c) - { - return _requestEventHelper.getList(c); - } - public SpecimenRequestEvent getRequestEvent(Container c, int rowId) { return _requestEventHelper.get(c, rowId); @@ -432,10 +425,7 @@ else if (oldInputs.length != newInputs.length) private String getSafeString(String str) { - if (str == null) - return ""; - else - return str; + return Objects.requireNonNullElse(str, ""); } public void createRequestStatus(User user, SpecimenRequestStatus status) @@ -472,11 +462,6 @@ public void deleteRequestRequirement(User user, SpecimenRequestRequirement requi requirement.delete(); } - public void createRequestRequirement(User user, SpecimenRequestRequirement requirement, boolean createEvent) throws AttachmentService.DuplicateFilenameException - { - createRequestRequirement(user, requirement, createEvent, false); - } - public void createRequestRequirement(User user, SpecimenRequestRequirement requirement, boolean createEvent, boolean force) throws AttachmentService.DuplicateFilenameException { SpecimenRequest request = getRequest(requirement.getContainer(), requirement.getRequestId()); @@ -575,7 +560,7 @@ public void updateRequest(User user, SpecimenRequest request) throws Requestabil // update specimen states List vials = request.getVials(); - if (vials.size() > 0) + if (!vials.isEmpty()) { SpecimenRequestStatus status = getRequestStatus(request.getContainer(), request.getStatusId()); updateSpecimenStatus(vials, user, status.isSpecimensLocked()); @@ -589,7 +574,7 @@ public void updateRequest(User user, SpecimenRequest request) throws Requestabil public void createRequestSpecimenMapping(User user, SpecimenRequest request, List vials, boolean createEvents, boolean createRequirements) throws RequestabilityManager.InvalidRuleException, AttachmentService.DuplicateFilenameException, SpecimenRequestException { - if (vials == null || vials.size() == 0) + if (vials == null || vials.isEmpty()) return; DbScope scope = SpecimenSchema.get().getScope(); @@ -636,13 +621,13 @@ private void updateSpecimenStatus(List vials, User user, boolean lockedInR Table.update(user, SpecimenSchema.get().getTableInfoVial(vial.getContainer()), vial.getRowMap(), vial.getRowId()); } updateRequestabilityAndCounts(vials, user); - if (vials.size() > 0) + if (!vials.isEmpty()) clearCaches(getContainer(vials)); } private void updateRequestabilityAndCounts(List vials, User user) throws RequestabilityManager.InvalidRuleException { - if (vials.size() == 0) + if (vials.isEmpty()) return; Container container = getContainer(vials); @@ -807,7 +792,7 @@ void clearRequestEventHelper(Container c) _requestEventHelper.clearCache(c); } - private static class GroupedValueColumnHelper + public static class GroupedValueColumnHelper { private final String _viewColumnName; private final String _sqlColumnName; @@ -822,26 +807,11 @@ public GroupedValueColumnHelper(String sqlColumnName, String viewColumnName, Str _joinColumnName = joinColumnName; } - public String getViewColumnName() - { - return _viewColumnName; - } - - public String getSqlColumnName() - { - return _sqlColumnName; - } - public String getUrlFilterName() { return _urlFilterName; } - public String getJoinColumnName() - { - return _joinColumnName; - } - public FieldKey getFieldKey() { // constructs FieldKey whether it needs join or not @@ -1163,11 +1133,11 @@ public void deleteRequest(User user, SpecimenRequest request) throws Requestabil public void deleteRequestSpecimenMappings(User user, SpecimenRequest request, List vialIds, boolean createEvents) throws RequestabilityManager.InvalidRuleException, AttachmentService.DuplicateFilenameException { - if (vialIds == null || vialIds.size() == 0) + if (vialIds == null || vialIds.isEmpty()) return; Set vialRowIds = new HashSet<>(vialIds); - List vials = SpecimenManagerNew.get().getVials(request.getContainer(), user, vialRowIds); + List vials = SpecimenManager.get().getVials(request.getContainer(), user, vialRowIds); List globalUniqueIds = new ArrayList<>(vials.size()); List descriptions = new ArrayList<>(); for (Vial vial : vials) diff --git a/specimen/src/org/labkey/specimen/SpecimenRequestStatus.java b/specimen/src/org/labkey/specimen/SpecimenRequestStatus.java new file mode 100644 index 00000000..76348c49 --- /dev/null +++ b/specimen/src/org/labkey/specimen/SpecimenRequestStatus.java @@ -0,0 +1,126 @@ +/* + * 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.specimen; + +import org.labkey.api.data.Container; +import org.labkey.api.study.AbstractStudyCachable; + +import java.util.Objects; + +public class SpecimenRequestStatus extends AbstractStudyCachable +{ + private int _rowId; + private Container _container; + private Integer _sortOrder; + private String _label; + private boolean _specimensLocked; + private boolean _finalState; + + @Override + public Container getContainer() + { + return _container; + } + + @Override + public void setContainer(Container container) + { + verifyMutability(); + _container = container; + } + + public String getLabel() + { + return _label; + } + + public void setLabel(String label) + { + verifyMutability(); + _label = label; + } + + @Override + public Object getPrimaryKey() + { + return getRowId(); + } + + public int getRowId() + { + return _rowId; + } + + public void setRowId(int rowId) + { + verifyMutability(); + _rowId = rowId; + } + + public Integer getSortOrder() + { + return _sortOrder; + } + + public void setSortOrder(Integer sortOrder) + { + verifyMutability(); + _sortOrder = sortOrder; + } + + public boolean isFinalState() + { + return _finalState; + } + + public void setFinalState(boolean finalState) + { + verifyMutability(); + _finalState = finalState; + } + + public boolean isSpecimensLocked() + { + return _specimensLocked; + } + + public void setSpecimensLocked(boolean specimensLocked) + { + verifyMutability(); + _specimensLocked = specimensLocked; + } + + public boolean isSystemStatus() + { + return _sortOrder < 0; + } + + @Override + public boolean equals(Object o) + { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + SpecimenRequestStatus that = (SpecimenRequestStatus) o; + return _rowId == that._rowId && _specimensLocked == that._specimensLocked && _finalState == that._finalState && Objects.equals(_container, that._container) && Objects.equals(_sortOrder, that._sortOrder) && Objects.equals(_label, that._label); + } + + @Override + public int hashCode() + { + return Objects.hash(_rowId, _container, _sortOrder, _label, _specimensLocked, _finalState); + } +} diff --git a/specimen/src/org/labkey/specimen/SpecimenServiceImpl.java b/specimen/src/org/labkey/specimen/SpecimenServiceImpl.java index 6354073b..28eb1180 100644 --- a/specimen/src/org/labkey/specimen/SpecimenServiceImpl.java +++ b/specimen/src/org/labkey/specimen/SpecimenServiceImpl.java @@ -42,11 +42,10 @@ import org.labkey.api.query.ValidationException; import org.labkey.api.security.User; import org.labkey.api.specimen.DefaultSpecimenTablesTemplate; -import org.labkey.api.specimen.SpecimenColumns; import org.labkey.api.specimen.SpecimenSchema; import org.labkey.api.specimen.Vial; -import org.labkey.api.specimen.importer.SimpleSpecimenImporter; -import org.labkey.api.specimen.importer.SpecimenColumn; +import org.labkey.specimen.importer.SimpleSpecimenImporter; +import org.labkey.specimen.importer.SpecimenColumn; import org.labkey.api.specimen.location.LocationCache; import org.labkey.api.specimen.model.SpecimenTablesProvider; import org.labkey.api.study.ParticipantVisit; @@ -473,7 +472,7 @@ public void fireSpecimensChanged(Container c, User user, Logger logger) @Override public void deleteAllSpecimenData(Container c, Set set, User user) { - // UNDONE: use transaction? + // Consider: Move this to a container listener? Stop clearing caches, since that's handled by SpecimenRequestContainerListener. SimpleFilter containerFilter = SimpleFilter.createContainerFilter(c); Table.delete(SpecimenSchema.get().getTableInfoSampleRequestSpecimen(), containerFilter); @@ -491,9 +490,6 @@ public void deleteAllSpecimenData(Container c, Set set, User user) SpecimenRequestManager.get().clearRequestStatusHelper(c); assert set.add(SpecimenSchema.get().getTableInfoSampleRequestStatus()); - new SpecimenTablesProvider(c, null, null).deleteTables(); - LocationCache.clear(c); - Table.delete(SpecimenSchema.get().getTableInfoSampleAvailabilityRule(), containerFilter); assert set.add(SpecimenSchema.get().getTableInfoSampleAvailabilityRule()); diff --git a/specimen/src/org/labkey/specimen/SpecimenTableManager.java b/specimen/src/org/labkey/specimen/SpecimenTableManager.java new file mode 100644 index 00000000..116caa3a --- /dev/null +++ b/specimen/src/org/labkey/specimen/SpecimenTableManager.java @@ -0,0 +1,198 @@ +package org.labkey.specimen; + +import org.labkey.api.data.Container; +import org.labkey.api.data.JdbcType; +import org.labkey.api.data.TableInfo; +import org.labkey.api.data.dialect.SqlDialect; +import org.labkey.api.exp.PropertyDescriptor; +import org.labkey.api.exp.property.Domain; +import org.labkey.api.exp.property.DomainProperty; +import org.labkey.api.security.User; +import org.labkey.api.specimen.SpecimenSchema; +import org.labkey.api.specimen.importer.EventVialRollup; +import org.labkey.specimen.importer.ImportTypes; +import org.labkey.api.specimen.importer.RollupHelper; +import org.labkey.api.specimen.importer.RollupHelper.RollupMap; +import org.labkey.specimen.importer.SpecimenColumn; +import org.labkey.specimen.importer.TargetTable; +import org.labkey.api.specimen.model.SpecimenTablesProvider; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +// Shared code used by both specimen writers and importers. Combine with SpecimenTablesProvider? +public class SpecimenTableManager +{ + // Event -> Vial Rollup map. Note: This is initialized at construction time in determineSpecimenColumns(). + private final RollupMap _eventToVialRollups = new RollupMap<>(); + private final Container _container; + private final User _user; + + // Provisioned specimen tables + private final TableInfo _tableInfoSpecimen; + private final TableInfo _tableInfoVial; + private final TableInfo _tableInfoSpecimenEvent; + private final TableInfo _tableInfoLocation; + private final TableInfo _tableInfoPrimaryType; + private final TableInfo _tableInfoDerivative; + private final TableInfo _tableInfoAdditive; + private final SqlDialect _dialect; + + public SpecimenTableManager(Container container, User user) + { + _container = container; + _user = user; + + _tableInfoSpecimenEvent = SpecimenSchema.get().getTableInfoSpecimenEvent(_container); + _tableInfoVial = SpecimenSchema.get().getTableInfoVial(_container); + _tableInfoSpecimen = SpecimenSchema.get().getTableInfoSpecimen(_container); + _tableInfoPrimaryType = SpecimenSchema.get().getTableInfoSpecimenPrimaryType(_container); + _tableInfoLocation = SpecimenSchema.get().getTableInfoLocation(_container); + _tableInfoDerivative = SpecimenSchema.get().getTableInfoSpecimenDerivative(_container); + _tableInfoAdditive = SpecimenSchema.get().getTableInfoSpecimenAdditive(_container); + _dialect = _tableInfoSpecimen.getSqlDialect(); + + _specimenColumns = determineSpecimenColumns(); + } + + protected SqlDialect getSqlDialect() + { + return _dialect; + } + + protected TableInfo getTableInfoSpecimen() + { + return _tableInfoSpecimen; + } + + protected TableInfo getTableInfoVial() + { + return _tableInfoVial; + } + + protected TableInfo getTableInfoSpecimenEvent() + { + return _tableInfoSpecimenEvent; + } + + protected TableInfo getTableInfoPrimaryType() + { + return _tableInfoPrimaryType; + } + protected TableInfo getTableInfoLocation() + { + return _tableInfoLocation; + } + + protected TableInfo getTableInfoDerivative() + { + return _tableInfoDerivative; + } + + protected TableInfo getTableInfoAdditive() + { + return _tableInfoAdditive; + } + + public TableInfo getTableInfoFromFkTableName(String fkTableName) + { + if ("Site".equalsIgnoreCase(fkTableName)) + return getTableInfoLocation(); + if ("SpecimenPrimaryType".equalsIgnoreCase(fkTableName)) + return getTableInfoPrimaryType(); + if ("SpecimenDerivative".equalsIgnoreCase(fkTableName)) + return getTableInfoDerivative(); + if ("SpecimenAdditive".equalsIgnoreCase(fkTableName)) + return getTableInfoAdditive(); + throw new IllegalStateException("Unexpected table name."); + } + + protected Container getContainer() + { + return _container; + } + + protected User getUser() + { + return _user; + } + + protected final Collection _specimenColumns; + + public Collection getSpecimenColumns() + { + return _specimenColumns; + } + + protected RollupMap getEventToVialRollups() + { + return _eventToVialRollups; + } + + private Collection determineSpecimenColumns() + { + Collection specimenColumns = new ArrayList<>(SpecimenColumns.BASE_SPECIMEN_COLUMNS); + + SpecimenTablesProvider specimenTablesProvider = new SpecimenTablesProvider(_container, _user, null); + + Domain vialDomain = specimenTablesProvider.getDomain("Vial", true); + if (null == vialDomain) + throw new IllegalStateException("Expected Vial domain to already be created."); + + List vialProperties = new ArrayList<>(); + for (DomainProperty domainProperty : vialDomain.getNonBaseProperties()) + vialProperties.add(domainProperty.getPropertyDescriptor()); + + Domain specimenEventDomain = specimenTablesProvider.getDomain("SpecimenEvent", true); + if (null == specimenEventDomain) + throw new IllegalStateException("Expected SpecimenEvent domain to already be created."); + + SqlDialect dialect = getTableInfoSpecimen().getSqlDialect(); + + Set eventBaseProperties = specimenEventDomain.getBaseProperties(); + for (DomainProperty domainProperty : specimenEventDomain.getProperties()) + { + PropertyDescriptor property = domainProperty.getPropertyDescriptor(); + if (!eventBaseProperties.contains(domainProperty)) + { + SpecimenColumn specimenColumn = new SpecimenColumn(property.getName().toLowerCase(), property.getImportAliasSet(), property.getStorageColumnName(), getTypeName(property, dialect), TargetTable.SPECIMEN_EVENTS); + specimenColumns.add(specimenColumn); + } + RollupHelper.findRollups(_eventToVialRollups, property, vialProperties, RollupHelper.getEventVialRollups(), false); + } + + return specimenColumns; + } + + private static final Map JDBCtoIMPORTER_TYPE = new HashMap<>(); + static + { + JDBCtoIMPORTER_TYPE.put(JdbcType.DATE, ImportTypes.DATETIME_TYPE); + JDBCtoIMPORTER_TYPE.put(JdbcType.TIMESTAMP, ImportTypes.DATETIME_TYPE); + JDBCtoIMPORTER_TYPE.put(JdbcType.TIME, ImportTypes.DURATION_TYPE); + JDBCtoIMPORTER_TYPE.put(JdbcType.DECIMAL, ImportTypes.NUMERIC_TYPE); + JDBCtoIMPORTER_TYPE.put(JdbcType.BOOLEAN, ImportTypes.BOOLEAN_TYPE); + JDBCtoIMPORTER_TYPE.put(JdbcType.BINARY, ImportTypes.BINARY_TYPE); + JDBCtoIMPORTER_TYPE.put(JdbcType.BIGINT, "BIGINT"); + JDBCtoIMPORTER_TYPE.put(JdbcType.INTEGER, "INT"); + JDBCtoIMPORTER_TYPE.put(JdbcType.REAL, "FLOAT"); + JDBCtoIMPORTER_TYPE.put(JdbcType.DOUBLE, null); + JDBCtoIMPORTER_TYPE.put(JdbcType.VARCHAR, "VARCHAR"); + } + + private String getTypeName(PropertyDescriptor property, SqlDialect dialect) + { + String typeName = JDBCtoIMPORTER_TYPE.get(property.getJdbcType()); + if (null == typeName) + typeName = dialect.getSqlTypeName(property.getJdbcType()); + if (null == typeName) + throw new UnsupportedOperationException("Unsupported JdbcType: " + property.getJdbcType().toString()); + if ("VARCHAR".equals(typeName)) + typeName = String.format("VARCHAR(%d)", property.getScale()); + return typeName; + } +} diff --git a/specimen/src/org/labkey/specimen/actions/ManageRequestBean.java b/specimen/src/org/labkey/specimen/actions/ManageRequestBean.java index 0488046b..0732f687 100644 --- a/specimen/src/org/labkey/specimen/actions/ManageRequestBean.java +++ b/specimen/src/org/labkey/specimen/actions/ManageRequestBean.java @@ -3,7 +3,6 @@ import org.labkey.api.data.ActionButton; import org.labkey.api.data.Container; import org.labkey.api.data.MenuButton; -import org.labkey.api.specimen.SpecimenRequestStatus; import org.labkey.api.specimen.Vial; import org.labkey.api.specimen.location.LocationImpl; import org.labkey.api.specimen.location.LocationManager; @@ -13,6 +12,7 @@ import org.labkey.api.view.DisplayElement; import org.labkey.api.view.ViewContext; import org.labkey.specimen.SpecimenRequestManager; +import org.labkey.specimen.SpecimenRequestStatus; import org.labkey.specimen.actions.SpecimenController.ImportVialIdsAction; import org.labkey.specimen.actions.SpecimenController.OverviewAction; import org.labkey.specimen.actions.SpecimenController.RemoveRequestSpecimensAction; diff --git a/specimen/src/org/labkey/specimen/actions/ParticipantCommentAction.java b/specimen/src/org/labkey/specimen/actions/ParticipantCommentAction.java index 6277560b..f052a92d 100644 --- a/specimen/src/org/labkey/specimen/actions/ParticipantCommentAction.java +++ b/specimen/src/org/labkey/specimen/actions/ParticipantCommentAction.java @@ -24,7 +24,6 @@ import org.labkey.api.security.User; import org.labkey.api.security.permissions.InsertPermission; import org.labkey.api.security.permissions.ReadPermission; -import org.labkey.api.specimen.SpecimenManagerNew; import org.labkey.api.specimen.Vial; import org.labkey.api.specimen.model.SpecimenComment; import org.labkey.api.study.InsertUpdateAction; @@ -67,7 +66,7 @@ public boolean handlePost(ParticipantCommentForm form, BindException errors) thr Container container = getContainer(); for (int rowId : form.getVialCommentsToClear()) { - Vial vial = SpecimenManagerNew.get().getVial(container, user, rowId); + Vial vial = SpecimenManager.get().getVial(container, user, rowId); if (vial != null) { SpecimenComment comment = SpecimenManager.get().getSpecimenCommentForVial(vial); diff --git a/specimen/src/org/labkey/specimen/actions/ShowUploadSpecimensAction.java b/specimen/src/org/labkey/specimen/actions/ShowUploadSpecimensAction.java index 9f7a40d8..c23f49c6 100644 --- a/specimen/src/org/labkey/specimen/actions/ShowUploadSpecimensAction.java +++ b/specimen/src/org/labkey/specimen/actions/ShowUploadSpecimensAction.java @@ -31,8 +31,7 @@ import org.labkey.api.security.RequiresPermission; import org.labkey.api.security.User; import org.labkey.api.security.permissions.AdminPermission; -import org.labkey.api.specimen.SpecimenManagerNew; -import org.labkey.api.specimen.importer.SimpleSpecimenImporter; +import org.labkey.specimen.importer.SimpleSpecimenImporter; import org.labkey.api.specimen.settings.RepositorySettings; import org.labkey.api.specimen.settings.SettingsManager; import org.labkey.api.study.Study; @@ -125,9 +124,9 @@ public boolean handlePost(UploadSpecimensForm form, BindException errors) throws columnAliases.put("additive", SimpleSpecimenImporter.ADDITIVE_TYPE); columnAliases.put("additiveType", SimpleSpecimenImporter.ADDITIVE_TYPE); columnAliases.put("additive Type", SimpleSpecimenImporter.ADDITIVE_TYPE); - columnAliases.put("derivative", SimpleSpecimenImporter.DERIVIATIVE_TYPE); - columnAliases.put("derivativeType", SimpleSpecimenImporter.DERIVIATIVE_TYPE); - columnAliases.put("derivative Type", SimpleSpecimenImporter.DERIVIATIVE_TYPE); + columnAliases.put("derivative", SimpleSpecimenImporter.DERIVATIVE_TYPE); + columnAliases.put("derivativeType", SimpleSpecimenImporter.DERIVATIVE_TYPE); + columnAliases.put("derivative Type", SimpleSpecimenImporter.DERIVATIVE_TYPE); columnAliases.put("drawTimestamp", SimpleSpecimenImporter.DRAW_TIMESTAMP); columnAliases.put("Draw Date", SimpleSpecimenImporter.DRAW_TIMESTAMP); columnAliases.put("Date", SimpleSpecimenImporter.DRAW_TIMESTAMP); diff --git a/specimen/src/org/labkey/specimen/actions/SpecimenApiController.java b/specimen/src/org/labkey/specimen/actions/SpecimenApiController.java index e091c857..56877835 100644 --- a/specimen/src/org/labkey/specimen/actions/SpecimenApiController.java +++ b/specimen/src/org/labkey/specimen/actions/SpecimenApiController.java @@ -31,10 +31,7 @@ import org.labkey.api.security.UserManager; import org.labkey.api.security.permissions.ReadPermission; import org.labkey.api.specimen.SpecimenManagerNew; -import org.labkey.api.specimen.SpecimenRequestException; -import org.labkey.api.specimen.SpecimenRequestStatus; import org.labkey.api.specimen.Vial; -import org.labkey.api.specimen.importer.RequestabilityManager; import org.labkey.api.specimen.location.LocationImpl; import org.labkey.api.specimen.location.LocationManager; import org.labkey.api.specimen.model.PrimaryType; @@ -48,7 +45,10 @@ import org.labkey.api.view.ViewContext; import org.labkey.specimen.RequestedSpecimens; import org.labkey.specimen.SpecimenManager; +import org.labkey.specimen.SpecimenRequestException; import org.labkey.specimen.SpecimenRequestManager; +import org.labkey.specimen.SpecimenRequestStatus; +import org.labkey.specimen.importer.RequestabilityManager; import org.labkey.specimen.model.AdditiveType; import org.labkey.specimen.model.DerivativeType; import org.labkey.specimen.requirements.SpecimenRequest; @@ -492,7 +492,7 @@ else if (IdTypes.RowId.name().equals(idType)) try { int id = Integer.parseInt(vialId); - vial = SpecimenManagerNew.get().getVial(getContainer(), getUser(), id); + vial = SpecimenManager.get().getVial(getContainer(), getUser(), id); } catch (NumberFormatException e) { diff --git a/specimen/src/org/labkey/specimen/actions/SpecimenController.java b/specimen/src/org/labkey/specimen/actions/SpecimenController.java index c8491c6b..75da7aa3 100644 --- a/specimen/src/org/labkey/specimen/actions/SpecimenController.java +++ b/specimen/src/org/labkey/specimen/actions/SpecimenController.java @@ -6,6 +6,7 @@ import jakarta.mail.internet.InternetAddress; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpSession; import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.LogManager; @@ -15,6 +16,7 @@ import org.json.JSONObject; import org.labkey.api.action.ApiResponse; import org.labkey.api.action.ApiSimpleResponse; +import org.labkey.api.action.ExportAction; import org.labkey.api.action.FormHandlerAction; import org.labkey.api.action.FormViewAction; import org.labkey.api.action.HasViewContext; @@ -82,18 +84,13 @@ import org.labkey.api.security.permissions.AdminPermission; import org.labkey.api.security.permissions.ReadPermission; import org.labkey.api.security.permissions.UpdatePermission; -import org.labkey.api.specimen.SpecimenManagerNew; +import org.labkey.api.specimen.SpecimenMigrationService; import org.labkey.api.specimen.SpecimenQuerySchema; -import org.labkey.api.specimen.SpecimenRequestException; -import org.labkey.api.specimen.SpecimenRequestStatus; import org.labkey.api.specimen.SpecimenSchema; import org.labkey.api.specimen.Vial; -import org.labkey.api.specimen.importer.RequestabilityManager; -import org.labkey.api.specimen.importer.SimpleSpecimenImporter; import org.labkey.api.specimen.location.LocationImpl; import org.labkey.api.specimen.location.LocationManager; import org.labkey.api.specimen.model.SpecimenComment; -import org.labkey.api.specimen.query.SpecimenQueryView; import org.labkey.api.specimen.security.permissions.EditSpecimenDataPermission; import org.labkey.api.specimen.security.permissions.ManageRequestSettingsPermission; import org.labkey.api.specimen.security.permissions.RequestSpecimensPermission; @@ -103,7 +100,6 @@ import org.labkey.api.specimen.settings.StatusSettings; import org.labkey.api.study.CohortFilter; import org.labkey.api.study.Dataset; -import org.labkey.api.study.MapArrayExcelWriter; import org.labkey.api.study.SpecimenService; import org.labkey.api.study.SpecimenTransform; import org.labkey.api.study.SpecimenUrls; @@ -152,7 +148,10 @@ import org.labkey.specimen.RequestEventType; import org.labkey.specimen.RequestedSpecimens; import org.labkey.specimen.SpecimenManager; +import org.labkey.specimen.SpecimenRequestException; import org.labkey.specimen.SpecimenRequestManager; +import org.labkey.specimen.SpecimenRequestStatus; +import org.labkey.specimen.importer.RequestabilityManager; import org.labkey.specimen.model.ExtendedSpecimenRequestView; import org.labkey.specimen.model.SpecimenRequestActor; import org.labkey.specimen.model.SpecimenRequestEvent; @@ -162,6 +161,7 @@ import org.labkey.specimen.pipeline.SpecimenArchive; import org.labkey.specimen.pipeline.SpecimenBatch; import org.labkey.specimen.query.SpecimenEventQueryView; +import org.labkey.specimen.query.SpecimenQueryView; import org.labkey.specimen.query.SpecimenRequestQueryView; import org.labkey.specimen.requirements.SpecimenRequest; import org.labkey.specimen.requirements.SpecimenRequestRequirement; @@ -1120,30 +1120,17 @@ public void addNavTrail(NavTree root) } @RequiresPermission(AdminPermission.class) - public class GetSpecimenExcelAction extends SimpleViewAction + public class GetSpecimenExcelAction extends ExportAction { @Override - public ModelAndView getView(Object o, BindException errors) - { - List> defaultSpecimens = new ArrayList<>(); - SimpleSpecimenImporter importer = new SimpleSpecimenImporter(getContainer(), getUser(), - getStudyRedirectIfNull().getTimepointType(), StudyService.get().getSubjectNounSingular(getContainer())); - MapArrayExcelWriter xlWriter = new MapArrayExcelWriter(defaultSpecimens, importer.getSimpleSpecimenColumns()); - // Note: I don't think this is having any effect on the output because ExcelColumn.renderCaption() uses - // the DisplayColumn's caption, not its own caption. That seems wrong... - xlWriter.setColumnModifier(col -> col.setCaption(importer.label(col.getName()))); - xlWriter.renderWorkbook(getViewContext().getResponse()); - - return null; - } - - @Override - public void addNavTrail(NavTree root) + public void export(Object o, HttpServletResponse response, BindException errors) throws Exception { + SpecimenMigrationService.get().exportSpecimens(getContainer(), getUser(), new ArrayList<>(), + getStudyRedirectIfNull().getTimepointType(), StudyService.get().getSubjectNounSingular(getContainer()), response); } } - static class SpecimenEventForm + public static class SpecimenEventForm { private String _id; private Container _targetStudy; @@ -1366,7 +1353,7 @@ public ModelAndView getView(ViewEventForm viewEventForm, BindException errors) { Study study = getStudyThrowIfNull(); _showingSelectedSpecimens = viewEventForm.isSelected(); - Vial vial = SpecimenManagerNew.get().getVial(getContainer(), getUser(), viewEventForm.getId()); + Vial vial = SpecimenManager.get().getVial(getContainer(), getUser(), viewEventForm.getId()); if (vial == null) throw new NotFoundException("Specimen " + viewEventForm.getId() + " does not exist."); @@ -1990,7 +1977,7 @@ public static List getSpecimensFromRowIds(long[] requestedSampleIds, Conta List vials = new ArrayList<>(); for (long requestedSampleId : requestedSampleIds) { - Vial current = SpecimenManagerNew.get().getVial(container, user, requestedSampleId); + Vial current = SpecimenManager.get().getVial(container, user, requestedSampleId); if (current != null) vials.add(current); } @@ -2440,7 +2427,7 @@ else if (form.getDestinationLocation() > 0) vials = new ArrayList<>(); for (long specimenId : specimenIds) { - Vial vial = SpecimenManagerNew.get().getVial(container, user, specimenId); + Vial vial = SpecimenManager.get().getVial(container, user, specimenId); if (vial != null) { boolean isAvailable = vial.isAvailable(); @@ -4123,7 +4110,7 @@ public boolean handlePost(UpdateParticipantCommentsForm commentsForm, BindExcept Container container = getContainer(); List vials = new ArrayList<>(); for (int rowId : commentsForm.getRowId()) - vials.add(SpecimenManagerNew.get().getVial(container, user, rowId)); + vials.add(SpecimenManager.get().getVial(container, user, rowId)); Map currentComments = SpecimenManager.get().getSpecimenComments(vials); @@ -4132,7 +4119,7 @@ public boolean handlePost(UpdateParticipantCommentsForm commentsForm, BindExcept { if (commentsForm.getCopySampleId() != -1) { - Vial vial = SpecimenManagerNew.get().getVial(container, user, commentsForm.getCopySampleId()); + Vial vial = SpecimenManager.get().getVial(container, user, commentsForm.getCopySampleId()); if (vial != null) { _successUrl = new ActionURL(CopyParticipantCommentAction.class, container). @@ -5223,7 +5210,7 @@ public boolean handlePost(EmailSpecimenListForm form, BindException errors) thro protected List getSpecimenList() { SimpleFilter filter = getSpecimenListFilter(getSpecimenRequest(), originatingOrProvidingLocation, type); - return SpecimenManagerNew.get().getVials(container, user, filter); + return SpecimenManager.get().getVials(container, user, filter); // return new TableSelector(StudySchema.getInstance().getTableInfoSpecimenDetail(container), filter, null).getArrayList(Specimen.class); } diff --git a/specimen/src/org/labkey/specimen/actions/SpecimenHeaderBean.java b/specimen/src/org/labkey/specimen/actions/SpecimenHeaderBean.java index 3b01b1d2..fe32d12d 100644 --- a/specimen/src/org/labkey/specimen/actions/SpecimenHeaderBean.java +++ b/specimen/src/org/labkey/specimen/actions/SpecimenHeaderBean.java @@ -5,7 +5,7 @@ import org.labkey.api.query.FieldKey; import org.labkey.api.query.QueryService; import org.labkey.api.specimen.SpecimenQuerySchema; -import org.labkey.api.specimen.query.SpecimenQueryView; +import org.labkey.specimen.query.SpecimenQueryView; import org.labkey.api.study.Study; import org.labkey.api.study.StudyService; import org.labkey.api.util.Pair; diff --git a/specimen/src/org/labkey/specimen/actions/SpecimenSearchBean.java b/specimen/src/org/labkey/specimen/actions/SpecimenSearchBean.java index 6e5cca8b..4c88a25b 100644 --- a/specimen/src/org/labkey/specimen/actions/SpecimenSearchBean.java +++ b/specimen/src/org/labkey/specimen/actions/SpecimenSearchBean.java @@ -16,20 +16,10 @@ package org.labkey.specimen.actions; -import org.jetbrains.annotations.Nullable; -import org.labkey.api.data.DisplayColumn; -import org.labkey.api.data.TableInfo; -import org.labkey.api.query.UserSchema; -import org.labkey.api.specimen.SpecimenQuerySchema; -import org.labkey.api.specimen.query.SpecimenQueryView; -import org.labkey.api.study.StudyService; -import org.labkey.api.util.DemoMode; +import org.labkey.specimen.query.SpecimenQueryView; import org.labkey.api.view.ActionURL; import org.labkey.api.view.ViewContext; -import java.util.ArrayList; -import java.util.List; - /** * User: brittp * Date: Oct 21, 2010 4:01:33 PM diff --git a/specimen/src/org/labkey/specimen/actions/SpecimenViewTypeForm.java b/specimen/src/org/labkey/specimen/actions/SpecimenViewTypeForm.java index cfff8bf5..a170962d 100644 --- a/specimen/src/org/labkey/specimen/actions/SpecimenViewTypeForm.java +++ b/specimen/src/org/labkey/specimen/actions/SpecimenViewTypeForm.java @@ -1,7 +1,7 @@ package org.labkey.specimen.actions; import org.labkey.api.action.QueryViewAction; -import org.labkey.api.specimen.query.SpecimenQueryView; +import org.labkey.specimen.query.SpecimenQueryView; public class SpecimenViewTypeForm extends QueryViewAction.QueryExportForm { diff --git a/specimen/src/org/labkey/specimen/actions/SpecimensViewBean.java b/specimen/src/org/labkey/specimen/actions/SpecimensViewBean.java index 9c93d066..c2ef7fb8 100644 --- a/specimen/src/org/labkey/specimen/actions/SpecimensViewBean.java +++ b/specimen/src/org/labkey/specimen/actions/SpecimensViewBean.java @@ -1,7 +1,7 @@ package org.labkey.specimen.actions; import org.labkey.api.specimen.Vial; -import org.labkey.api.specimen.query.SpecimenQueryView; +import org.labkey.specimen.query.SpecimenQueryView; import org.labkey.api.view.ViewContext; import java.util.List; diff --git a/specimen/src/org/labkey/specimen/actions/ViewRequestsHeaderBean.java b/specimen/src/org/labkey/specimen/actions/ViewRequestsHeaderBean.java index b7888b51..df2ee08d 100644 --- a/specimen/src/org/labkey/specimen/actions/ViewRequestsHeaderBean.java +++ b/specimen/src/org/labkey/specimen/actions/ViewRequestsHeaderBean.java @@ -1,8 +1,8 @@ package org.labkey.specimen.actions; -import org.labkey.api.specimen.SpecimenRequestStatus; import org.labkey.api.view.ViewContext; import org.labkey.specimen.SpecimenRequestManager; +import org.labkey.specimen.SpecimenRequestStatus; import org.labkey.specimen.query.SpecimenRequestQueryView; import java.util.List; diff --git a/specimen/src/org/labkey/specimen/importer/AbstractSpecimenTask.java b/specimen/src/org/labkey/specimen/importer/AbstractSpecimenTask.java index d48db7d1..ab8de4cb 100644 --- a/specimen/src/org/labkey/specimen/importer/AbstractSpecimenTask.java +++ b/specimen/src/org/labkey/specimen/importer/AbstractSpecimenTask.java @@ -26,7 +26,6 @@ import org.labkey.api.pipeline.RecordedActionSet; import org.labkey.api.pipeline.TaskFactory; import org.labkey.api.query.ValidationException; -import org.labkey.api.specimen.importer.SpecimenImporter; import org.labkey.api.study.SpecimenService; import org.labkey.api.study.SpecimenTransform; import org.labkey.api.study.Study; diff --git a/specimen/src/org/labkey/specimen/importer/DefaultSpecimenImportStrategyFactory.java b/specimen/src/org/labkey/specimen/importer/DefaultSpecimenImportStrategyFactory.java index 3331ff1c..bb9ad140 100644 --- a/specimen/src/org/labkey/specimen/importer/DefaultSpecimenImportStrategyFactory.java +++ b/specimen/src/org/labkey/specimen/importer/DefaultSpecimenImportStrategyFactory.java @@ -18,7 +18,6 @@ import org.labkey.api.data.Container; import org.labkey.api.data.DbSchema; -import org.labkey.api.specimen.importer.StandardSpecimenImportStrategy; import org.labkey.api.study.SpecimenImportStrategy; import org.labkey.api.study.SpecimenImportStrategyFactory; import org.labkey.api.writer.VirtualFile; diff --git a/specimen/src/org/labkey/specimen/importer/EditableSpecimenImporter.java b/specimen/src/org/labkey/specimen/importer/EditableSpecimenImporter.java new file mode 100644 index 00000000..6f475eb1 --- /dev/null +++ b/specimen/src/org/labkey/specimen/importer/EditableSpecimenImporter.java @@ -0,0 +1,274 @@ +/* + * 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.specimen.importer; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.jetbrains.annotations.NotNull; +import org.labkey.api.collections.CaseInsensitiveHashSet; +import org.labkey.api.data.Container; +import org.labkey.api.data.DbSchema; +import org.labkey.api.data.SQLFragment; +import org.labkey.api.data.SqlExecutor; +import org.labkey.api.data.TableInfo; +import org.labkey.api.query.ValidationException; +import org.labkey.api.security.User; +import org.labkey.api.specimen.SpecimenSchema; +import org.labkey.api.specimen.importer.EventVialRollup; +import org.labkey.api.specimen.importer.RollupHelper; +import org.labkey.api.specimen.importer.RollupHelper.RollupMap; +import org.labkey.api.specimen.importer.RollupInstance; +import org.labkey.api.study.SpecimenImportStrategy; +import org.labkey.specimen.SpecimenColumns; +import org.labkey.specimen.SpecimenRequestManager; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * User: davebradlee + * Date: 6/13/13 + * Time: 12:13 PM + */ +public class EditableSpecimenImporter extends SpecimenImporter +{ + private static final String GUID_COLNAME = "GlobalUniqueId"; + + private final boolean _insert; + + public EditableSpecimenImporter(Container container, User user, boolean insert) + { + super(container, user); + _insert = insert; + } + + public void process(List> rows, boolean merge) throws IOException, ValidationException + { + process(rows, merge, null); + } + + public void process(List> rows, boolean merge, Logger logger) throws IOException, ValidationException + { + if (null == logger) + logger = LogManager.getLogger(getClass()); + _process(rows, merge, logger); + } + + private void _process(List> rows, boolean merge, Logger logger) throws IOException, ValidationException + { + if (merge) + { + _generateGlobalUniqueIds = markEventsObsolete(rows); // Generate this many new ids (needed if not present) + } + + List> specimenRows = new ArrayList<>(); + for (Map row : rows) + { + Map specimenRow = new HashMap<>(); + for (String colName : row.keySet()) + { + specimenRow.put(colName, row.get(colName)); + } + specimenRows.add(specimenRow); + } + + SpecimenImportStrategy strategy = new StandardSpecimenImportStrategy(getContainer()); + Map sifMap = new HashMap<>(); + addSpecimenImportFile(sifMap, _specimensTableType, specimenRows, strategy); + + try + { + super.process(sifMap, merge, logger, null, true, true); + } + catch (ValidationException ex) + { + if (logger != null) + logger.error("Error during import", ex); + throw ex; + } + } + + + private void addSpecimenImportFile(Map fileNameMap, SpecimenTableType type, List> specimenRows, SpecimenImportStrategy strategy) + { + assert null != type : "Unknown type!"; + fileNameMap.put(type, new IteratorSpecimenImportFile(specimenRows, strategy, type)); + } + + public List> mapColumnNamesToTsvColumnNames(List> rows) + { + List> newRows = new ArrayList<>(); + + Set seenFields = new CaseInsensitiveHashSet(); + for (Map row : rows) + { + Map newRow = new HashMap<>(); + for (String columnName : row.keySet()) + { + Object value = row.get(columnName); + if (null != value) + { + columnName = getMappedColumnName(columnName); + SpecimenColumn specCol = findSpecimenColumnFromDbName(columnName); + if (null != specCol) + { + newRow.put(specCol.getPrimaryTsvColumnName(), value); + seenFields.add(specCol.getPrimaryTsvColumnName()); + } + } + } + newRows.add(newRow); + } + + // Make sure every row has each of seenFields + for (Map row : newRows) + { + for (String field : seenFields) + if (!row.containsKey(field)) + row.put(field, null); + } + return newRows; + } + + private Map _dbNameToSpecimenColumnMap; + + private SpecimenColumn findSpecimenColumnFromDbName(String name) + { + if (null == _dbNameToSpecimenColumnMap) + { + _dbNameToSpecimenColumnMap = new HashMap<>(); + for (SpecimenColumn col : getSpecimenColumns()) + _dbNameToSpecimenColumnMap.put(col.getDbColumnName().toLowerCase(), col); + } + + return _dbNameToSpecimenColumnMap.get(name.toLowerCase()); + } + + @NotNull + public String getMappedColumnName(@NotNull String columnName) + { + String specialName = getSpecialColumnName(columnName); + if (null != specialName) + return specialName; + return columnName.toLowerCase(); + } + + private Map _specialColumnNameMap; + + private String getSpecialColumnName(String name) + { + if (null == _specialColumnNameMap) + { + _specialColumnNameMap = new HashMap<>(); + _specialColumnNameMap.put("participantid", "ptid"); + _specialColumnNameMap.put("primarytype", "primarytypeid"); + _specialColumnNameMap.put("additivetype", "additivetypeid"); + _specialColumnNameMap.put("derivativetype", "derivativetypeid"); + _specialColumnNameMap.put("derivativetype2", "derivativetypeid2"); + _specialColumnNameMap.put("sequencenum", "visitvalue"); + _specialColumnNameMap.put("sitename", "labid"); + _specialColumnNameMap.put("clinic", "originatinglocationid"); + _specialColumnNameMap.put("firstprocessedbyinitials", "processedbyinitials"); + _specialColumnNameMap.put("latestcomments", "comments"); + _specialColumnNameMap.put("latestqualitycomments", "qualitycomments"); + + // Add any rollups from optionals whose names are not matching + RollupMap matchedRollups = RollupHelper.getEventToVialRollups(getContainer(), getUser()); + for (Map.Entry>> entry : matchedRollups.entrySet()) + { + String fromName = entry.getKey(); + List> rollupItems = entry.getValue(); + for (RollupInstance rollupItem : rollupItems) + { + String toName = rollupItem.first; + if (!fromName.equalsIgnoreCase(toName)) + _specialColumnNameMap.put(toName, fromName); + } + } + } + + return _specialColumnNameMap.get(name.toLowerCase()); + } + + @Override + protected void remapTempTableLookupIndexes(DbSchema schema, String tempTable, List loadedColumns) + { + // do nothing + } + + @Override + protected void checkForConflictingSpecimens(DbSchema schema, String tempTable, List loadedColumns) + { + // Only check if inserting + if (_insert) + super.checkForConflictingSpecimens(schema, tempTable, loadedColumns); + } + + private int markEventsObsolete(List> rows) + { + Container container = getContainer(); + TableInfo tableInfoSpecimenEvent = SpecimenSchema.get().getTableInfoSpecimenEvent(container); + TableInfo tableInfoVial = SpecimenSchema.get().getTableInfoVial(container); + + SQLFragment sqlPrefix = new SQLFragment(); + sqlPrefix.append("UPDATE ").append(tableInfoSpecimenEvent) + .append(" SET Obsolete = ").append(tableInfoSpecimenEvent.getSqlDialect().getBooleanTRUE()) + .append(" WHERE VialId IN ") + .append("(SELECT RowId FROM ").append(tableInfoVial) + .append(" WHERE Obsolete = ").append(tableInfoSpecimenEvent.getSqlDialect().getBooleanFALSE()) + .append(" AND " + GUID_COLNAME).append(" "); + + int noGuidRowCount = 0; + int guidRowCount = 0; + ArrayList guids = new ArrayList<>(); + for (Map row : rows) + { + String guid = (String)row.get(SpecimenColumns.GLOBAL_UNIQUE_ID_TSV_COL); + if (null == guid) + { + noGuidRowCount++; + continue; + } + guidRowCount++; + guids.add(guid); + if (guids.size() == 1000) + { + SQLFragment sql = new SQLFragment(sqlPrefix); + tableInfoSpecimenEvent.getSqlDialect().appendInClauseSql(sql, guids); + sql.append(")"); + new SqlExecutor(SpecimenSchema.get().getSchema()).execute(sql); + guids.clear(); + } + } + if (!guids.isEmpty()) + { + SQLFragment sql = new SQLFragment(sqlPrefix); + tableInfoSpecimenEvent.getSqlDialect().appendInClauseSql(sql, guids); + sql.append(")"); + new SqlExecutor(SpecimenSchema.get().getSchema()).execute(sql); + } + + if (guidRowCount > 0) + { + SpecimenRequestManager.get().clearCaches(container); + } + return noGuidRowCount; + } +} diff --git a/specimen/src/org/labkey/specimen/importer/FileSystemSpecimenImportFile.java b/specimen/src/org/labkey/specimen/importer/FileSystemSpecimenImportFile.java new file mode 100644 index 00000000..711187ba --- /dev/null +++ b/specimen/src/org/labkey/specimen/importer/FileSystemSpecimenImportFile.java @@ -0,0 +1,81 @@ +/* + * 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.specimen.importer; + +import org.labkey.api.reader.Readers; +import org.labkey.api.reader.TabLoader; +import org.labkey.api.study.SpecimenImportStrategy; +import org.labkey.api.util.Filter; +import org.labkey.api.writer.VirtualFile; + +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; +import java.util.Map; + +/* +* User: adam +* Date: Feb 10, 2013 +* Time: 5:16:12 PM +*/ +public class FileSystemSpecimenImportFile implements SpecimenImportFile +{ + private final VirtualFile _dir; + private final String _fileName; + private final SpecimenImportStrategy _strategy; + private final SpecimenTableType _tableType; + + public FileSystemSpecimenImportFile(VirtualFile dir, String fileName, SpecimenImportStrategy strategy, SpecimenTableType tableType) + { + _dir = dir; + _fileName = fileName; + _strategy = strategy; + _tableType = tableType; + } + + @Override + public SpecimenTableType getTableType() + { + return _tableType; + } + + @Override + public SpecimenImportStrategy getStrategy() + { + return _strategy; + } + + @Override + public TabLoader getDataLoader() throws IOException + { + Reader reader = Readers.getReader(getInputStream()); + TabLoader loader = new TabLoader(reader, true, null, true); // Close on complete + loader.setInferTypes(false); + + Filter> filter = getStrategy().getImportFilter(); + + if (null != filter) + loader.setMapFilter(filter); + + return loader; + } + + private InputStream getInputStream() throws IOException + { + return _dir.getInputStream(_fileName); + } +} diff --git a/specimen/src/org/labkey/specimen/importer/ImportTypes.java b/specimen/src/org/labkey/specimen/importer/ImportTypes.java new file mode 100644 index 00000000..6e91ea1a --- /dev/null +++ b/specimen/src/org/labkey/specimen/importer/ImportTypes.java @@ -0,0 +1,17 @@ +package org.labkey.specimen.importer; + +import org.labkey.api.specimen.SpecimenSchema; + +// Extracted from SpecimenImporter to ease the specimen module migration +public class ImportTypes +{ + private ImportTypes() + { + } + + public static final String DATETIME_TYPE = "SpecimenImporter/DateTime"; + public static final String DURATION_TYPE = "SpecimenImporter/TimeOnlyDate"; + public static final String NUMERIC_TYPE = "NUMERIC(15,4)"; + public static final String BOOLEAN_TYPE = SpecimenSchema.get().getSqlDialect().getBooleanDataType(); + public static final String BINARY_TYPE = SpecimenSchema.get().getSqlDialect().getBinaryDataType(); +} diff --git a/specimen/src/org/labkey/specimen/importer/ImportableColumn.java b/specimen/src/org/labkey/specimen/importer/ImportableColumn.java new file mode 100644 index 00000000..eb51de36 --- /dev/null +++ b/specimen/src/org/labkey/specimen/importer/ImportableColumn.java @@ -0,0 +1,174 @@ +package org.labkey.specimen.importer; + +import org.jetbrains.annotations.Nullable; +import org.labkey.api.data.JdbcType; +import org.labkey.api.data.dialect.SqlDialect; +import org.labkey.api.exp.PropertyDescriptor; +import org.labkey.api.reader.ColumnDescriptor; +import org.labkey.api.specimen.SpecimenSchema; +import org.labkey.api.util.TimeOnlyDate; + +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.List; + +public class ImportableColumn +{ + protected final String _dbType; + + private final String _tsvColumnName; + private final Collection _tsvColumnAliases; + private final String _dbColumnName; + private final boolean _maskOnExport; + private final boolean _unique; + private final int _size; + private final Class _javaClass; + private final JdbcType _jdbcType; + private Object _defaultValue = null; + + public ImportableColumn(String tsvColumnName, String dbColumnName, String databaseType) + { + this(tsvColumnName, dbColumnName, databaseType, false); + } + + public ImportableColumn(String tsvColumnName, String dbColumnName, String databaseType, Object defaultValue) + { + this(tsvColumnName, dbColumnName, databaseType, false); + _defaultValue = defaultValue; + } + + public ImportableColumn(String tsvColumnName, String dbColumnName, String databaseType, boolean unique) + { + this(tsvColumnName, dbColumnName, databaseType, unique, false); + } + + public ImportableColumn(String tsvColumnName, String dbColumnName, String databaseType, boolean unique, boolean maskOnExport) + { + this(tsvColumnName, Collections.emptyList(), dbColumnName, databaseType, unique, maskOnExport); + } + + public ImportableColumn(String tsvColumnName, Collection tsvColumnAliases, String dbColumnName, String databaseType, boolean unique, boolean maskOnExport) + { + _tsvColumnName = tsvColumnName; + _tsvColumnAliases = List.copyOf(tsvColumnAliases); + _dbColumnName = dbColumnName; + _unique = unique; + _maskOnExport = maskOnExport; + + switch (databaseType) + { + case ImportTypes.DURATION_TYPE: + _dbType = SpecimenSchema.get().getSqlDialect().getDefaultDateTimeDataType(); + _javaClass = TimeOnlyDate.class; + break; + case ImportTypes.DATETIME_TYPE: + _dbType = SpecimenSchema.get().getSqlDialect().getDefaultDateTimeDataType(); + _javaClass = Date.class; + break; + default: + _dbType = databaseType.toUpperCase(); + _javaClass = determineJavaType(_dbType); + break; + } + + _jdbcType = JdbcType.valueOf(getJavaClass()); + + if (_dbType.startsWith("VARCHAR(")) + { + assert _dbType.charAt(_dbType.length() - 1) == ')' : "Unexpected VARCHAR type format: " + _dbType; + String sizeStr = _dbType.substring(8, _dbType.length() - 1); + _size = Integer.parseInt(sizeStr); + } + else + { + _size = -1; + } + } + + // Can't use standard JdbcType.valueOf() method since this uses contains() + private static Class determineJavaType(String dbType) + { + if (dbType.contains(ImportTypes.DATETIME_TYPE)) + throw new IllegalStateException("Java types for DateTime/Timestamp columns should be previously initialized."); + + if (dbType.contains("VARCHAR")) + return String.class; + else if (dbType.contains("FLOAT") || dbType.contains("DOUBLE") || dbType.contains(ImportTypes.NUMERIC_TYPE)) + return Double.class; + else if (dbType.contains("BIGINT")) + return Long.class; + else if (dbType.contains("INT")) + return Integer.class; + else if (dbType.contains(ImportTypes.BOOLEAN_TYPE)) + return Boolean.class; + else if (dbType.contains(ImportTypes.BINARY_TYPE)) + return byte[].class; + else if (dbType.contains("DATE")) + return Date.class; + else if (dbType.contains("TIME")) + return Date.class; + else + throw new UnsupportedOperationException("Unrecognized sql type: " + dbType); + } + + public ColumnDescriptor getColumnDescriptor() + { + return new ColumnDescriptor(_tsvColumnName, getJavaClass()); + } + + public String getDbColumnName() + { + return _dbColumnName; + } + + private String _legalDbColumnName; + + public String getLegalDbColumnName(SqlDialect dialect) + { + if (null == _legalDbColumnName) + _legalDbColumnName = PropertyDescriptor.getLegalSelectNameFromStorageName(dialect, getDbColumnName()); + return _legalDbColumnName; + } + + // Preferred TSV column name. For example, the name that gets exported. + public String getPrimaryTsvColumnName() + { + return _tsvColumnName; + } + + public Collection getImportAliases() + { + return _tsvColumnAliases; + } + + public boolean isUnique() + { + return _unique; + } + + public Class getJavaClass() + { + return _javaClass; + } + + public JdbcType getJdbcType() + { + return _jdbcType; + } + + public int getMaxSize() + { + return _size; + } + + public boolean isMaskOnExport() + { + return _maskOnExport; + } + + public @Nullable Object getDefaultValue() + { + return _defaultValue; + } +} diff --git a/specimen/src/org/labkey/specimen/importer/IteratorSpecimenImportFile.java b/specimen/src/org/labkey/specimen/importer/IteratorSpecimenImportFile.java new file mode 100644 index 00000000..97c9de6a --- /dev/null +++ b/specimen/src/org/labkey/specimen/importer/IteratorSpecimenImportFile.java @@ -0,0 +1,61 @@ +/* + * 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.specimen.importer; + +import org.labkey.api.reader.DataLoader; +import org.labkey.api.reader.MapLoader; +import org.labkey.api.study.SpecimenImportStrategy; + +import java.util.List; +import java.util.Map; + +/* +* User: adam +* Date: Feb 16, 2013 +* Time: 7:35:23 AM +*/ +public class IteratorSpecimenImportFile implements SpecimenImportFile +{ + private final SpecimenImportStrategy _strategy; + private final List> _rows; + private final SpecimenTableType _tableType; + + public IteratorSpecimenImportFile(List> rows, SpecimenImportStrategy strategy, SpecimenTableType tableType) + { + _strategy = strategy; + _rows = rows; + _tableType = tableType; + } + + @Override + public SpecimenTableType getTableType() + { + return _tableType; + } + + @Override + public SpecimenImportStrategy getStrategy() + { + return _strategy; + } + + @Override + public DataLoader getDataLoader() + { + return new MapLoader(_rows); + } +} diff --git a/specimen/src/org/labkey/specimen/importer/RequestabilityManager.java b/specimen/src/org/labkey/specimen/importer/RequestabilityManager.java new file mode 100644 index 00000000..7e0f9aec --- /dev/null +++ b/specimen/src/org/labkey/specimen/importer/RequestabilityManager.java @@ -0,0 +1,856 @@ +/* + * Copyright (c) 2010-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.specimen.importer; + +import org.apache.logging.log4j.Logger; +import org.jetbrains.annotations.Nullable; +import org.labkey.api.data.ColumnInfo; +import org.labkey.api.data.CompareType; +import org.labkey.api.data.Container; +import org.labkey.api.data.DbSchema; +import org.labkey.api.data.DbScope; +import org.labkey.api.data.SQLFragment; +import org.labkey.api.data.SimpleFilter; +import org.labkey.api.data.Sort; +import org.labkey.api.data.SqlExecutor; +import org.labkey.api.data.Table; +import org.labkey.api.data.TableInfo; +import org.labkey.api.data.TableSelector; +import org.labkey.api.query.CustomView; +import org.labkey.api.query.FieldKey; +import org.labkey.api.query.QueryAction; +import org.labkey.api.query.QueryParam; +import org.labkey.api.query.QueryService; +import org.labkey.api.query.QueryView; +import org.labkey.api.query.UserSchema; +import org.labkey.api.security.User; +import org.labkey.api.specimen.SpecimenSchema; +import org.labkey.api.specimen.Vial; +import org.labkey.api.study.SpecimenUrls; +import org.labkey.api.util.PageFlowUtil; +import org.labkey.api.view.ActionURL; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * User: brittp + * Date: Mar 19, 2010 12:48:46 PM + */ +public class RequestabilityManager +{ + public static class RuleBean + { + private Integer _rowId; // SERIAL NOT NULL + private Container _container; // EntityId NOT NULL + private int _sortOrder; // INTEGER NOT NULL + private RuleType _ruleType; // VARCHAR(50) + private String _ruleData; // VARCHAR(250) + private MarkType _markType; // VARCHAR(30) + + public RuleBean() + { + // no-arg constructor for creation via Table-layer reflection + } + + public RuleBean(RequestableRule rule) + { + _container = rule.getContainer(); + _ruleType = rule.getType(); + _ruleData = rule.getRuleData(); + if (_ruleData != null && _ruleData.length() > 250) + throw new IllegalArgumentException("Rule data is currently limited to 250 characters."); + _markType = rule.getMarkType(); + } + + public Integer getRowId() + { + return _rowId; + } + + public void setRowId(Integer rowId) + { + _rowId = rowId; + } + + public Container getContainer() + { + return _container; + } + + public void setContainer(Container container) + { + _container = container; + } + + public int getSortOrder() + { + return _sortOrder; + } + + public void setSortOrder(int sortOrder) + { + _sortOrder = sortOrder; + } + + public String getRuleType() + { + return _ruleType.name(); + } + + public void setRuleType(String ruleType) + { + _ruleType = RuleType.valueOf(ruleType); + } + + public String getMarkType() + { + return _markType.name(); + } + + public void setMarkType(String markType) + { + _markType = MarkType.valueOf(markType); + } + + public String getRuleData() + { + return _ruleData; + } + + public void setRuleData(String ruleData) + { + _ruleData = ruleData; + } + + public RequestableRule createRule() + { + return _ruleType.createRule(_container, _ruleData); + } + } + + public enum MarkType + { + AVAILABLE + { + @Override + public String getLabel() + { + return "Available"; + } + }, + + UNAVAILABLE + { + @Override + public String getLabel() + { + return "Unavailable"; + } + }, + + AVAILABLE_OR_UNAVAILABLE + { + @Override + public String getLabel() + { + return "Available or unavailable"; + } + }; + + public abstract String getLabel(); + } + + + public enum RuleType + { + ADMIN_OVERRIDE + { + @Override + public RequestableRule createRule(Container container, String ruleData) + { + return new AdminOverrideRule(container); + } + + @Override + public String getName() + { + return "Administrator Override"; + } + + @Override + public String getDescription() + { + return "Marks vials available or unavailable based on the 'requestable' column in the specimen data feed."; + } + + @Override + public MarkType getDefaultMarkType() + { + return MarkType.AVAILABLE_OR_UNAVAILABLE; + } + + @Override + public ActionURL getDefaultTestURL(Container container) + { + ActionURL testURL = PageFlowUtil.urlProvider(SpecimenUrls.class).getSpecimensURL(container, true); + testURL.addFilter("SpecimenDetail", FieldKey.fromParts("Requestable"), CompareType.NONBLANK, null); + return testURL; + } + }, + AT_REPOSITORY + { + @Override + public RequestableRule createRule(Container container, String ruleData) + { + return new RepositoryRule(container); + } + + @Override + public String getName() + { + return "At Repository Check"; + } + + @Override + public String getDescription() + { + return "Marks vials unavailable if they are not currently held by a repository."; + } + + @Override + public MarkType getDefaultMarkType() + { + return MarkType.UNAVAILABLE; + } + + @Override + public ActionURL getDefaultTestURL(Container container) + { + ActionURL testURL = PageFlowUtil.urlProvider(SpecimenUrls.class).getSpecimensURL(container, true); + testURL.addFilter("SpecimenDetail", FieldKey.fromParts("AtRepository"), CompareType.EQUAL, Boolean.FALSE); + return testURL; + + } + }, + LOCKED_IN_REQUEST + { + @Override + public RequestableRule createRule(Container container, String ruleData) + { + return new LockedInRequestRule(container); + } + + @Override + public String getName() + { + return "Locked In Request Check"; + } + + @Override + public String getDescription() + { + return "Marks vials unavailable if they are part of an active specimen request."; + } + + @Override + public MarkType getDefaultMarkType() + { + return MarkType.UNAVAILABLE; + } + + @Override + public ActionURL getDefaultTestURL(Container container) + { + ActionURL testURL = PageFlowUtil.urlProvider(SpecimenUrls.class).getSpecimensURL(container, true); + testURL.addFilter("SpecimenDetail", FieldKey.fromParts("LockedInRequest"), CompareType.EQUAL, Boolean.TRUE); + return testURL; + + } + }, + LOCKED_WHILE_PROCESSING + { + @Override + public RequestableRule createRule(Container container, String ruleData) + { + return new LockedWhileProcessingRule(container); + } + + @Override + public String getName() + { + return "Locked While Processing Check"; + } + + @Override + public String getDescription() + { + return "Marks vials unavailable if they are being processed."; + } + + @Override + public MarkType getDefaultMarkType() + { + return MarkType.UNAVAILABLE; + } + + @Override + public ActionURL getDefaultTestURL(Container container) + { + return null; + } + }, + CUSTOM_QUERY + { + @Override + public RequestableRule createRule(Container container, String ruleData) + { + return new CustomQueryRule(container, ruleData); + } + + @Override + public String getName() + { + return "Custom Query"; + } + + @Override + public String getDescription() + { + return "Marks vials available or unavailable based on the results of an administrator-defined query."; + } + + @Override + public MarkType getDefaultMarkType() + { + return null; + } + + @Override + public ActionURL getDefaultTestURL(Container container) + { + return null; + } + }; + + public abstract RequestableRule createRule(Container container, String ruleData); + + public abstract String getName(); + + public abstract String getDescription(); + + public abstract MarkType getDefaultMarkType(); + + @Nullable + public abstract ActionURL getDefaultTestURL(Container container); + } + + private static final RequestabilityManager _instance = new RequestabilityManager(); + + private RequestabilityManager() + { + // private constructor to ensure use of singleton. + } + + public static class InvalidRuleException extends Exception + { + public InvalidRuleException(String msg) + { + super(msg); + } + + public InvalidRuleException(String msg, Exception cause) + { + super(msg, cause); + } + } + + public static abstract class RequestableRule + { + protected Container _container; + + public RequestableRule(Container container) + { + _container = container; + } + + public abstract SQLFragment getAvailabilityReason(); + + public int updateRequestability(User user, List vials) throws InvalidRuleException + { + TableInfo tableInfoVial = SpecimenSchema.get().getTableInfoVial(_container); + if (null == tableInfoVial) + throw new IllegalStateException("Expected Vial table to exist."); + + SQLFragment reason = getAvailabilityReason(); + SQLFragment updateSQL = new SQLFragment("UPDATE "); + updateSQL.append(tableInfoVial).append(" SET "); + updateSQL.append(getAvailableAssignmentSQL()); + updateSQL.append(", AvailabilityReason = "); + updateSQL.append(reason); + updateSQL.append(" WHERE "); + updateSQL.append(getFilterSQL(_container, user, vials)); + return new SqlExecutor(SpecimenSchema.get().getSchema()).execute(updateSQL); + } + + protected SQLFragment getAvailableAssignmentSQL() + { + MarkType type = getMarkType(); + if (!(type == MarkType.AVAILABLE || type == MarkType.UNAVAILABLE)) + throw new IllegalStateException("Rules that do not specify a simple mark type must override getUpdateSQL."); + + return new SQLFragment("Available = ?", type == MarkType.AVAILABLE ? Boolean.TRUE : Boolean.FALSE); + } + + protected SQLFragment getGlobalUniqueIdInSQL(List vials) + { + SQLFragment sql = new SQLFragment(); + if (vials != null && !vials.isEmpty()) + { + sql.append("GlobalUniqueId "); + sql.append(getSpecimenGlobalUniqueIdSet(vials)); + } + return sql; + } + + protected SQLFragment getSpecimenGlobalUniqueIdSet(List vials) + { + List specimenIDs = new ArrayList<>(vials.size()); + for (Vial vial : vials) + { + specimenIDs.add(vial.getGlobalUniqueId()); + } + return SpecimenSchema.get().getSqlDialect().appendInClauseSql(new SQLFragment(), specimenIDs); + } + + protected abstract SQLFragment getFilterSQL(Container container, User user, List vials) throws InvalidRuleException; + + public MarkType getMarkType() + { + return getType().getDefaultMarkType(); + } + + public String getRuleData() + { + return null; + } + + public abstract RuleType getType(); + + @Nullable + public ActionURL getTestURL(User user) + { + return getType().getDefaultTestURL(_container); + } + + public String getName() + { + String name = getType().getName(); + String extra = getExtraName(); + if (extra != null) + name += ": " + extra; + return name; + } + + public Container getContainer() + { + return _container; + } +/* + If the rule's type isn't enough to identify the rule, getExtraName can be used to provide the user + with additional information. + */ + protected String getExtraName() + { + return null; + } + } + + public static final String CUSTOM_QUERY_DATA_SEPARATOR = "~"; + + private static class CustomQueryRule extends RequestableRule + { + private final String _schemaName; + private final String _queryName; + private String _viewName; + private final boolean _markRequestable; + + protected CustomQueryRule(Container container, String state) + { + super(container); + String[] values = state.split(CUSTOM_QUERY_DATA_SEPARATOR); + _schemaName = values[0]; + _queryName = values[1]; + _viewName = values[2]; + if (_viewName != null && _viewName.length() == 0) + _viewName = null; + _markRequestable = Boolean.parseBoolean(values[3]); + } + + @Override + public ActionURL getTestURL(User user) + { + ActionURL url = QueryService.get().urlFor(user, _container, QueryAction.executeQuery, _schemaName, _queryName); + if (_viewName != null) + url.addParameter(QueryView.DATAREGIONNAME_DEFAULT + "." + QueryParam.viewName.name(), _viewName); + return url; + } + + @Override + public MarkType getMarkType() + { + return _markRequestable ? MarkType.AVAILABLE : MarkType.UNAVAILABLE; + } + + @Override + public String getRuleData() + { + return _schemaName + CUSTOM_QUERY_DATA_SEPARATOR + + _queryName + CUSTOM_QUERY_DATA_SEPARATOR + + (_viewName != null ? _viewName : "") + CUSTOM_QUERY_DATA_SEPARATOR + + _markRequestable; + } + + @Override + public SQLFragment getFilterSQL(Container container, User user, List vials) throws InvalidRuleException + { + SimpleFilter viewFilter = new SimpleFilter(); + if (_viewName != null) + { + CustomView baseView = QueryService.get().getCustomView(user, container, user, _schemaName, _queryName, _viewName); + if (baseView != null) + { + // copy our saved view filter into our SimpleFilter via an ActionURL (yuck...) + ActionURL url = new ActionURL(); + baseView.applyFilterAndSortToURL(url, "mockDataRegion"); + viewFilter.addUrlFilters(url, "mockDataRegion"); + } + else + throw new InvalidRuleException("Could not find view " + _viewName + " on query " + _queryName + " in schema " + _schemaName + "."); + } + + if (vials != null && !vials.isEmpty()) + { + Set globalUniqueIds = new HashSet<>(); + for (Vial vial : vials) + globalUniqueIds.add(vial.getGlobalUniqueId()); + viewFilter.addCondition(new SimpleFilter.InClause(FieldKey.fromParts("GlobalUniqueId"), globalUniqueIds)); + } + + UserSchema schema = QueryService.get().getUserSchema(user, container, _schemaName); + TableInfo tinfo = schema.getTable(_queryName); + if (null == tinfo) + throw new InvalidRuleException("Query " + _queryName + " in schema " + _schemaName + " not found."); + ColumnInfo globalUniqueIdCol = tinfo.getColumn("GlobalUniqueId"); + if (globalUniqueIdCol == null) + throw new InvalidRuleException("Query " + _queryName + " in schema " + _schemaName + " doesn't include required column name 'GlobalUniqueId'."); + + SQLFragment vialListSql = Table.getSelectSQL(tinfo, Collections.singleton(globalUniqueIdCol), viewFilter, null); + + SQLFragment updateFilter = new SQLFragment("GlobalUniqueId IN (SELECT GlobalUniqueId FROM ("); + updateFilter.append(vialListSql); + updateFilter.append(") X )"); + + return updateFilter; + } + + @Override + public SQLFragment getAvailabilityReason() + { + return new SQLFragment("'This vial is " + getMarkType().getLabel().toLowerCase() + " because it was found in the set called \"" + _queryName + "\".'"); + } + + @Override + public String getExtraName() + { + return _schemaName + "." + _queryName + (_viewName != null ? ", view " + _viewName : ""); + } + + @Override + public RuleType getType() + { + return RuleType.CUSTOM_QUERY; + } + } + + private static class RepositoryRule extends RequestableRule + { + public RepositoryRule(Container container) + { + super(container); + } + + @Override + public SQLFragment getFilterSQL(Container container, User user, List vials) + { + SQLFragment sql = new SQLFragment("AtRepository = ?", Boolean.FALSE); + if (vials != null && vials.size() > 0) + sql.append(" AND ").append(getGlobalUniqueIdInSQL(vials)); + return sql; + } + + @Override + public RuleType getType() + { + return RuleType.AT_REPOSITORY; + } + + @Override + public SQLFragment getAvailabilityReason() + { + return new SQLFragment("'This vial is unavailable because it is not currently held by a repository.'"); + } + } + + private static class AdminOverrideRule extends RequestableRule + { + public AdminOverrideRule(Container container) + { + super(container); + } + + @Override + public SQLFragment getAvailabilityReason() + { + SQLFragment sql = new SQLFragment("CASE WHEN Requestable = ? THEN 'This vial''s availability status was set by an administrator. Please contact an administrator for more information.' ELSE ' ' END"); + sql.add(false); + return sql; + } + + @Override + public RuleType getType() + { + return RuleType.ADMIN_OVERRIDE; + } + + @Override + public SQLFragment getFilterSQL(Container container, User user, List vials) + { + SQLFragment sql = new SQLFragment("Requestable IS NOT NULL"); + if (vials != null && vials.size() > 0) + sql.append(" AND ").append(getGlobalUniqueIdInSQL(vials)); + return sql; + } + + @Override + protected SQLFragment getAvailableAssignmentSQL() + { + return new SQLFragment("Available = Requestable"); + } + } + + private static class LockedInRequestRule extends RequestableRule + { + public LockedInRequestRule(Container container) + { + super(container); + } + + @Override + public SQLFragment getFilterSQL(Container container, User user, List vials) + { + SQLFragment sql = new SQLFragment("LockedInRequest = ?", Boolean.TRUE); + if (vials != null && vials.size() > 0) + sql.append(" AND ").append(getGlobalUniqueIdInSQL(vials)); + return sql; + } + + @Override + public RuleType getType() + { + return RuleType.LOCKED_IN_REQUEST; + } + + @Override + public SQLFragment getAvailabilityReason() + { + return new SQLFragment("'This vial is unavailable because it is locked in a specimen request.'"); + } + } + + private static class LockedWhileProcessingRule extends RequestableRule + { + public LockedWhileProcessingRule(Container container) + { + super(container); + } + + @Override + public SQLFragment getFilterSQL(Container container, User user, List vials) + { + TableInfo tableInfoVial = SpecimenSchema.get().getTableInfoVial(_container); + if (null == tableInfoVial) + throw new IllegalStateException("Expected Vial table to exist."); + + SQLFragment sql = new SQLFragment(); + if (vials != null && !vials.isEmpty()) + sql.append(getGlobalUniqueIdInSQL(vials)).append(" AND "); + + sql.append(" RowId IN (SELECT SV.RowId FROM ").append(tableInfoVial).append(" SV") + .append(" LEFT OUTER JOIN (SELECT Container, SampleRequestId, SpecimenGlobalUniqueId FROM ") + .append(SpecimenSchema.get().getTableInfoSampleRequestSpecimen()) + .append(") SRS ON (GlobalUniqueId = SpecimenGlobalUniqueId)") + .append(" LEFT OUTER JOIN (SELECT RowId, StatusId FROM ") + .append(SpecimenSchema.get().getTableInfoSampleRequest()) + .append(") SR ON (SR.RowId = SRS.SampleRequestId)") + .append(" LEFT OUTER JOIN (SELECT RowId, FinalState FROM ") + .append(SpecimenSchema.get().getTableInfoSampleRequestStatus()) + .append(") SRST ON (SRST.RowId = SR.StatusId)") + .append(" WHERE SRS.Container = ? AND SRST.FinalState = ?"); + sql.add(container.getId()); + sql.add(false); + if (vials != null && !vials.isEmpty()) + sql.append(" AND SpecimenGlobalUniqueId ").append(getSpecimenGlobalUniqueIdSet(vials)); + + sql.append(")"); + return sql; + } + + @Override + public RuleType getType() + { + return RuleType.LOCKED_WHILE_PROCESSING; + } + + @Override + public SQLFragment getAvailabilityReason() + { + return new SQLFragment("'This vial is unavailable because it is being processed.'"); + } + } + + public static RequestabilityManager getInstance() + { + return _instance; + } + + public List getRules(Container container) + { + TableInfo ruleTableInfo = SpecimenSchema.get().getTableInfoSampleAvailabilityRule(); + List ruleBeans = new TableSelector(ruleTableInfo, + SimpleFilter.createContainerFilter(container), + new Sort("SortOrder")).getArrayList(RuleBean.class); + List rules = new ArrayList<>(); + for (RuleBean bean : ruleBeans) + rules.add(bean.createRule()); + + return rules; + } + + public void saveRules(Container container, User user, List rules) + { + TableInfo ruleTableInfo = SpecimenSchema.get().getTableInfoSampleAvailabilityRule(); + DbScope scope = SpecimenSchema.get().getScope(); + try (DbScope.Transaction transaction = scope.ensureTransaction()) + { + Table.delete(ruleTableInfo, new SimpleFilter(FieldKey.fromString("Container"), container.getId())); + + int sortOrder = 0; + for (RequestableRule rule : rules) + { + RuleBean bean = new RuleBean(rule); + bean.setSortOrder(sortOrder++); + Table.insert(user, ruleTableInfo, bean); + } + + transaction.commit(); + } + } + + public void setDefaultRules(Container container, User user) + { + List rules = new ArrayList<>(); + rules.add(new RepositoryRule(container)); + rules.add(new AdminOverrideRule(container)); + rules.add(new LockedInRequestRule(container)); + saveRules(container, user, rules); + } + + private void updateRequestability(Container container, User user, boolean resetToAvailable, Logger logger, List vials) throws InvalidRuleException + { + DbSchema schema = SpecimenSchema.get().getSchema(); + assert schema.getScope().isTransactionActive() : "Requestability should always be updated within a transaction wrapping the addition/removal of vials."; + if (logger != null) + logger.info("Updating vial availability..."); + + if (resetToAvailable) + { + TableInfo tableInfoVial = SpecimenSchema.get().getTableInfoVial(container); + if (null == tableInfoVial) + throw new IllegalStateException("Expected Vial table to exist."); + + if (logger != null) + logger.info("\tResetting vials to default available state."); + SQLFragment updateSQL = new SQLFragment("UPDATE "); + updateSQL.append(tableInfoVial) + .append(" SET Available = ?, AvailabilityReason = ? "); + updateSQL.add(Boolean.TRUE); + updateSQL.add(null); + if (vials != null && vials.size() > 0) + { + updateSQL.append("WHERE RowId IN ("); + String sep = ""; + for (Vial vial : vials) + { + updateSQL.append(sep).append("?"); + sep = ", "; + updateSQL.add(vial.getRowId()); + } + updateSQL.append(")"); + } + new SqlExecutor(schema).execute(updateSQL); + + if (logger != null) + logger.info("\tReset complete."); + } + + for (RequestableRule rule : getRules(container)) + { + String action = rule.getMarkType().getLabel().toLowerCase(); + if (logger != null) + logger.info("\tMarking vials " + action + " based on " + rule.getName()); + int updatedCount = rule.updateRequestability(user, vials); + if (logger != null) + logger.info("\t" + updatedCount + " vials marked " + action + "."); + } + + if (logger != null) + logger.info("Vial availability update complete."); + + } + + public void updateRequestability(Container container, User user, List vials) throws InvalidRuleException + { + updateRequestability(container, user, true, null, vials); + } + + public void updateRequestability(Container container, User user, boolean resetToAvailable, boolean editingSpecimens, Logger logger) throws InvalidRuleException + { + updateRequestability(container, user, (resetToAvailable || editingSpecimens), logger, null); + } + + public static String makeSpecimenUnavailableMessage(Vial vial, @Nullable String additionalText) + { + String message = String.format("Specimen %s%s%s", + vial.getGlobalUniqueId(), + null != vial.getAvailabilityReason() ? vial.getAvailabilityReason().replaceFirst("This vial", "") : " is not available.", + null != additionalText ? " " + additionalText : ""); + return message; + } +} diff --git a/specimen/src/org/labkey/specimen/importer/SimpleSpecimenImporter.java b/specimen/src/org/labkey/specimen/importer/SimpleSpecimenImporter.java new file mode 100644 index 00000000..2ae5d345 --- /dev/null +++ b/specimen/src/org/labkey/specimen/importer/SimpleSpecimenImporter.java @@ -0,0 +1,445 @@ +/* + * Copyright (c) 2008-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.specimen.importer; + +import org.apache.commons.beanutils.ConvertUtils; +import org.apache.logging.log4j.Logger; +import org.labkey.api.collections.CaseInsensitiveHashMap; +import org.labkey.api.data.Container; +import org.labkey.api.data.Filter; +import org.labkey.api.data.RuntimeSQLException; +import org.labkey.api.data.SQLFragment; +import org.labkey.api.data.SimpleFilter; +import org.labkey.api.data.Sort; +import org.labkey.api.data.SqlExecutor; +import org.labkey.api.data.TableInfo; +import org.labkey.api.data.TableSelector; +import org.labkey.api.query.ValidationException; +import org.labkey.api.reader.ColumnDescriptor; +import org.labkey.api.reader.TabLoader; +import org.labkey.api.security.User; +import org.labkey.api.specimen.SpecimenSchema; +import org.labkey.api.study.SpecimenImportStrategy; +import org.labkey.api.study.Study; +import org.labkey.api.study.StudyService; +import org.labkey.api.study.StudyUtils; +import org.labkey.api.study.TimepointType; +import org.labkey.api.util.PageFlowUtil; +import org.labkey.api.util.logging.LogHelper; +import org.springframework.jdbc.BadSqlGrammarException; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public class SimpleSpecimenImporter extends SpecimenImporter +{ + public static final String VIAL_ID = "global_unique_specimen_id"; + public static final String SAMPLE_ID = "specimen_number"; + public static final String DRAW_TIMESTAMP = "draw_timestamp"; + public static final String VISIT = "visit_value"; + public static final String VOLUME = "volume"; + public static final String UNITS = "volume_units"; + public static final String PRIMARY_SPECIMEN_TYPE = "primary_specimen_type"; + public static final String DERIVATIVE_TYPE = "derivative_type"; + public static final String ADDITIVE_TYPE = "additive_type"; + public static final String PARTICIPANT_ID = "ptid"; + + private static final Logger LOG = LogHelper.getLogger(SimpleSpecimenImporter.class, "Specimen import status and errors"); + private static final Map DEFAULT_COLUMN_LABELS = new CaseInsensitiveHashMap<>(); + + static + { + DEFAULT_COLUMN_LABELS.put(VIAL_ID, "Global Unique Id"); + DEFAULT_COLUMN_LABELS.put(SAMPLE_ID, "Sample Id"); + DEFAULT_COLUMN_LABELS.put(DRAW_TIMESTAMP, "Draw Timestamp"); + DEFAULT_COLUMN_LABELS.put(VISIT, "Visit"); + DEFAULT_COLUMN_LABELS.put(VOLUME, "Volume"); + DEFAULT_COLUMN_LABELS.put(UNITS, "Volume Units"); + DEFAULT_COLUMN_LABELS.put(PRIMARY_SPECIMEN_TYPE, "Primary Type"); + DEFAULT_COLUMN_LABELS.put(ADDITIVE_TYPE, "Additive Type"); + DEFAULT_COLUMN_LABELS.put(DERIVATIVE_TYPE, "Derivative Type"); + DEFAULT_COLUMN_LABELS.put(PARTICIPANT_ID, "Subject Id"); + } + + private final Map _columnLabels; + private final TimepointType _timepointType; + + public SimpleSpecimenImporter(Container container, User user) + { + this(container, user, TimepointType.DATE, "Subject"); + } + + public SimpleSpecimenImporter(Container container, User user, TimepointType timepointType, String participantIdLabel) + { + super(container, user); + _timepointType = timepointType; + _columnLabels = new HashMap<>(DEFAULT_COLUMN_LABELS); + _columnLabels.put(PARTICIPANT_ID, participantIdLabel + " Id"); + } + + public String label(String columnName) + { + String str = _columnLabels.get(columnName); + return str == null ? columnName : str; + } + + public Map getColumnLabels() + { + return _columnLabels; + } + + public void fixupSpecimenColumns(TabLoader tl) throws IOException + { + ColumnDescriptor[] cols = tl.getColumns(); + ColumnDescriptor[] mappedCols = new ColumnDescriptor[cols.length]; + + for (int i = 0; i < cols.length; i++) + { + ColumnDescriptor col = cols[i]; + SpecimenColumn specCol = findSpecimenColumn(col.name); + if (null == specCol) + { + //Default cols should be type string so lookups can be created + col.clazz = String.class; + mappedCols[i] = col; + } + else + mappedCols[i] = specCol.getColumnDescriptor(); + + } + tl.setColumns(mappedCols); + } + + // UNDONE: Converting values belongs in _process + public List> fixupSpecimenRows(List> rows) + { + List> result = new ArrayList<>(); + for (Map row : rows) + result.add(fixupSpecimenRow(row)); + return result; + } + + private Map fixupSpecimenRow(Map row) + { + Map result = new HashMap<>(); + for (String key : row.keySet()) + { + Object value = row.get(key); + if (value instanceof String) + { + SpecimenColumn specCol = findSpecimenColumn(key); + Class type = String.class; + if (null != specCol) + type = specCol.getColumnDescriptor().clazz; + value = ConvertUtils.convert((String)value, type); + } + result.put(key, value); + } + + return result; + } + + public ColumnDescriptor[] getSimpleSpecimenColumns() + { + List colNames = new ArrayList<>(Arrays.asList( + VIAL_ID, + SAMPLE_ID, + DRAW_TIMESTAMP, + PARTICIPANT_ID, + VOLUME, + UNITS, + PRIMARY_SPECIMEN_TYPE, + DERIVATIVE_TYPE, + ADDITIVE_TYPE)); + if (_timepointType == TimepointType.VISIT) + colNames.add(3, VISIT); + + ColumnDescriptor[] cols = new ColumnDescriptor[colNames.size()]; + + for (int i = 0; i < cols.length; i++) + { + SpecimenColumn specCol = findSpecimenColumn(colNames.get(i)); + cols[i] = null == specCol ? new ColumnDescriptor(colNames.get(i), String.class) : specCol.getColumnDescriptor(); + } + + return cols; + } + + public void process(List> rows, boolean merge) throws IOException, ValidationException + { + //Map from column name to + Container container = getContainer(); + Study study = StudyService.get().getStudy(container); + Map lookupTables = new HashMap<>(); + lookupTables.put("additive_type", new LookupTable(SpecimenSchema.get().getTableInfoSpecimenAdditive(container), container, _additivesTableType, "additive_type_id", "additive_id", "additive", "Additive")); + lookupTables.put("derivative_type", new LookupTable(SpecimenSchema.get().getTableInfoSpecimenDerivative(container), container, _derivativesTableType, "derivative_type_id", "derivative_id", "derivative", "Derivative")); + lookupTables.put("primary_specimen_type", new LookupTable(SpecimenSchema.get().getTableInfoSpecimenPrimaryType(container), container, _primaryTypesTableType, "primary_specimen_type_id", "primary_type_id", "primary_type", "PrimaryType")); + LabLookupTable labLookup = new LabLookupTable(SpecimenSchema.get().getTableInfoLocation(container), container, _labsTableType); + lookupTables.put("lab", labLookup); + + List> specimenRows = new ArrayList<>(); + int recordId = 1; + + for (Map row : rows) + { + Map specimenRow = new HashMap<>(); + for (String colName : row.keySet()) + { + //If this column should be mapped to an integer lookup, do so + LookupTable lookupTable = lookupTables.get(colName); + if (null == lookupTable) + specimenRow.put(colName, row.get(colName)); + else + specimenRow.put(lookupTable.getForeignKeyCol(), lookupTable.getVal((String) row.get(colName))); + } + //Make specimen requestable + if (null == row.get("lab")) + specimenRow.put(labLookup.getForeignKeyCol(), labLookup.getDefaultLabId()); + + if (study.getTimepointType() != TimepointType.VISIT) + specimenRow.put(VISIT, StudyUtils.sequenceNumFromDate((Date) specimenRow.get(DRAW_TIMESTAMP))); + + if (!row.containsKey(VIAL_ID)) + specimenRow.put(VIAL_ID, specimenRow.get(SAMPLE_ID)); + specimenRow.put("record_id", recordId++); + specimenRows.add(specimenRow); + } + + SpecimenImportStrategy strategy = new StandardSpecimenImportStrategy(container); + Map sifMap = new HashMap<>(); + addSpecimenImportFile(sifMap, _specimensTableType, specimenRows, strategy); + for (LookupTable lookupTable : lookupTables.values()) + addSpecimenImportFile(sifMap, lookupTable.getTableType(), lookupTable.toMaps(), strategy); + + try + { + super.process(sifMap, merge, LOG, null, true, false); + } + catch (ValidationException ex) + { + LOG.error("Error during import", ex); + throw ex; + } + catch (BadSqlGrammarException ex) + { + if (areDuplicateLabIds(labLookup)) + throw new RuntimeException("Error possibly caused by duplicate ExternalIds in Location tables: " + ex.getSQLException().getMessage()); + throw new RuntimeSQLException(ex.getSQLException()); + } + } + + + private void addSpecimenImportFile(Map fileNameMap, SpecimenTableType type, List> specimenRows, SpecimenImportStrategy strategy) + { + assert null != type : "Unknown type!"; + fileNameMap.put(type, new IteratorSpecimenImportFile(specimenRows, strategy, type)); + } + + + private static class LookupTable + { + private final SpecimenTableType tableType; + private final String foreignKeyCol; + private final String tsvIdCol; + private final String tsvLabelCol; + // additive, derivative, primarytype, and site all use ExternalId as the id column. + private final String dbIdCol = "ExternalId"; + private final String dbRowIdCol = "RowId"; + private final String dbLabelCol; + + // Maps of label -> id + private final HashMap existingKeyMap = new HashMap<>(); + private final HashMap keyMap = new HashMap<>(); + private int minId = 0; + private int lastId = 0; + + LookupTable(TableInfo table, Container c, SpecimenTableType tableType, String foreignKeyCol, String tsvIdCol, String tsvLabelCol, String dbLabelCol) + { + this.tableType = tableType; + this.foreignKeyCol = foreignKeyCol; + this.tsvIdCol = tsvIdCol; + this.tsvLabelCol = tsvLabelCol; + this.dbLabelCol = dbLabelCol; + + getKeyMap(table, c); + } + + private void getKeyMap(TableInfo table, Container c) + { + final List> missingExternalId = new ArrayList<>(); + Filter filter = SimpleFilter.createContainerFilter(c); + new TableSelector(table, PageFlowUtil.set(dbRowIdCol, dbIdCol, dbLabelCol), filter, new Sort("RowId")).forEachMap(map -> { + Integer id = (Integer)map.get(dbIdCol); + if (id == null) + { + missingExternalId.add(map); + } + else + { + existingKeyMap.put((String)map.get(dbLabelCol), id); + minId = Math.min(minId, id); + lastId = Math.max(lastId, id); + } + }); + + // UNDONE: Temporary fix for 11.1: Create fake ExternalId for rows with null ExternalId + for (Map map : missingExternalId) + { + Integer rowId = (Integer)map.get(dbRowIdCol); + int id = --minId; + existingKeyMap.put((String)map.get(dbLabelCol), id); + + SQLFragment sql = new SQLFragment() + .append("UPDATE ") + .append(table.getSelectName()) + .append(" SET ") + .append(dbIdCol) + .append(" = ?") + .add(id) + .append(" WHERE ") + .append(dbRowIdCol) + .append(" = ?") + .add(rowId); + + new SqlExecutor(table.getSchema()).execute(sql); + } + } + + Integer getVal(String label) + { + Integer i = keyMap.get(label); + if (null == i) + { + i = existingKeyMap.get(label); + if (null == i) + { + i = lastId = lastId+1; + keyMap.put(label, i); + } + else + { + keyMap.put(label, i); + } + } + return i; + } + + List> toMaps() + { + List> maps = new ArrayList<>(keyMap.size()); + + for (Map.Entry entry : keyMap.entrySet()) + { + Map m = new HashMap<>(); + m.put(tsvLabelCol, entry.getKey()); + m.put(tsvIdCol, entry.getValue()); + maps.add(m); + } + + maps.sort((a, b) -> + { + Integer aId = (Integer) a.get(tsvIdCol); + Integer bId = (Integer) b.get(tsvIdCol); + return aId.compareTo(bId); + }); + + return maps; + } + + public SpecimenTableType getTableType() + { + return tableType; + } + + public String getTsvIdCol() + { + return tsvIdCol; + } + + public String getForeignKeyCol() + { + return foreignKeyCol; + } + + public Map getExistingKeyMap() + { + return existingKeyMap; + } + } + + private static class LabLookupTable extends LookupTable + { + static final String DEFAULT_LAB = "Not Specified"; + private final Integer defaultLabId; + + LabLookupTable(TableInfo tableInfo, Container c, SpecimenTableType tableType) + { + super(tableInfo, c, tableType, "lab_id", "lab_id", "lab_name", "Label"); + defaultLabId = getVal(DEFAULT_LAB); + } + + @Override + List> toMaps() + { + List> maps = super.toMaps(); + //Treat each like a repository so that specimens might be requestable. + for (Map m : maps) + { + m.put("is_repository", Boolean.TRUE); + } + return maps; + } + + @Override + Integer getVal(String label) + { + return null == label ? defaultLabId : super.getVal(label); + } + + Integer getDefaultLabId() + { + return defaultLabId; + } + } + + private Map specimenColumnMap; + + private SpecimenColumn findSpecimenColumn(String name) + { + if (null == specimenColumnMap) + specimenColumnMap = SpecimenImporter.createImportMap(getSpecimenColumns()); + return specimenColumnMap.get(name); + } + + private static boolean areDuplicateLabIds(LabLookupTable labLookupTable) + { + Set labids = new HashSet<>(); + for (Integer id : labLookupTable.getExistingKeyMap().values()) + { + if (!labids.add(id)) + return true; // already there + } + return false; + } +} diff --git a/specimen/src/org/labkey/specimen/importer/SpecimenColumn.java b/specimen/src/org/labkey/specimen/importer/SpecimenColumn.java new file mode 100644 index 00000000..2cec7dce --- /dev/null +++ b/specimen/src/org/labkey/specimen/importer/SpecimenColumn.java @@ -0,0 +1,110 @@ +package org.labkey.specimen.importer; + +import org.labkey.api.util.TimeOnlyDate; + +import java.util.Collection; +import java.util.Collections; + +public class SpecimenColumn extends ImportableColumn +{ + private final TargetTable _targetTable; + + private String _fkTable; + private String _joinType; + private String _fkColumn; + private String _aggregateEventFunction; + private boolean _isKeyColumn = false; + + public SpecimenColumn(String tsvColumnName, Collection tsvColumnAliases, String dbColumnName, String databaseType, TargetTable eventColumn, boolean unique) + { + super(tsvColumnName, tsvColumnAliases, dbColumnName, databaseType, unique, false); + _targetTable = eventColumn; + } + + public SpecimenColumn(String tsvColumnName, String dbColumnName, String databaseType, TargetTable eventColumn, boolean unique) + { + this(tsvColumnName, Collections.emptyList(), dbColumnName, databaseType, eventColumn, unique); + } + + public SpecimenColumn(String tsvColumnName, String dbColumnName, String databaseType, boolean isKeyColumn, TargetTable eventColumn, boolean unique) + { + this(tsvColumnName, dbColumnName, databaseType, eventColumn, unique); + _isKeyColumn = isKeyColumn; + } + + public SpecimenColumn(String tsvColumnName, String dbColumnName, String databaseType, boolean isKeyColumn, TargetTable eventColumn) + { + this(tsvColumnName, dbColumnName, databaseType, eventColumn, false); + _isKeyColumn = isKeyColumn; + } + + public SpecimenColumn(String tsvColumnName, String dbColumnName, String databaseType, TargetTable eventColumn) + { + this(tsvColumnName, dbColumnName, databaseType, eventColumn, false); + } + + public SpecimenColumn(String tsvColumnName, Collection tsvColumnAliases, String dbColumnName, String databaseType, TargetTable eventColumn) + { + this(tsvColumnName, tsvColumnAliases, dbColumnName, databaseType, eventColumn, false); + } + + public SpecimenColumn(String tsvColumnName, String dbColumnName, String databaseType, TargetTable eventColumn, String aggregateEventFunction) + { + this(tsvColumnName, dbColumnName, databaseType, eventColumn, false); + _aggregateEventFunction = aggregateEventFunction; + } + + public SpecimenColumn(String tsvColumnName, String dbColumnName, String databaseType, + TargetTable eventColumn, String fkTable, String fkColumn, String joinType) + { + this(tsvColumnName, dbColumnName, databaseType, eventColumn, false); + _fkColumn = fkColumn; + _fkTable = fkTable; + _joinType = joinType; + } + + public TargetTable getTargetTable() + { + return _targetTable; + } + + public String getFkColumn() + { + return _fkColumn; + } + + public String getFkTable() + { + return _fkTable; + } + + public String getJoinType() + { + return _joinType; + } + + public String getDbType() + { + return _dbType; + } + + public String getAggregateEventFunction() + { + return _aggregateEventFunction; + } + + public boolean isKeyColumn() + { + return _isKeyColumn; + } + + public String getFkTableAlias() + { + return getDbColumnName() + "Lookup"; + } + + public boolean isDateType() + { + return getDbType() != null && (getDbType().equals("DATETIME") || getDbType().equals("TIMESTAMP")) && !getJavaClass().equals(TimeOnlyDate.class); + } +} diff --git a/specimen/src/org/labkey/specimen/importer/SpecimenImportFile.java b/specimen/src/org/labkey/specimen/importer/SpecimenImportFile.java new file mode 100644 index 00000000..8102249c --- /dev/null +++ b/specimen/src/org/labkey/specimen/importer/SpecimenImportFile.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2013 LabKey Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.labkey.specimen.importer; + +import org.labkey.api.reader.DataLoader; +import org.labkey.api.study.SpecimenImportStrategy; + +import java.io.IOException; + +/* +* User: adam +* Date: Feb 16, 2013 +* Time: 6:58:21 AM +*/ +public interface SpecimenImportFile +{ + SpecimenImportStrategy getStrategy(); + SpecimenTableType getTableType(); + DataLoader getDataLoader() throws IOException; +} diff --git a/specimen/src/org/labkey/specimen/importer/SpecimenImporter.java b/specimen/src/org/labkey/specimen/importer/SpecimenImporter.java new file mode 100644 index 00000000..beedf7df --- /dev/null +++ b/specimen/src/org/labkey/specimen/importer/SpecimenImporter.java @@ -0,0 +1,3127 @@ +/* + * Copyright (c) 2008-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.specimen.importer; + +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.mutable.MutableInt; +import org.apache.logging.log4j.Logger; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.labkey.api.collections.CaseInsensitiveHashMap; +import org.labkey.api.collections.CaseInsensitiveHashSet; +import org.labkey.api.data.BaseColumnInfo; +import org.labkey.api.data.ColumnInfo; +import org.labkey.api.data.Container; +import org.labkey.api.data.DbSchema; +import org.labkey.api.data.DbScope; +import org.labkey.api.data.DbSequence; +import org.labkey.api.data.DbSequenceManager; +import org.labkey.api.data.JdbcType; +import org.labkey.api.data.Parameter; +import org.labkey.api.data.Results; +import org.labkey.api.data.ResultsImpl; +import org.labkey.api.data.RuntimeSQLException; +import org.labkey.api.data.SQLFragment; +import org.labkey.api.data.SimpleFilter; +import org.labkey.api.data.Sort; +import org.labkey.api.data.SqlExecutor; +import org.labkey.api.data.SqlSelector; +import org.labkey.api.data.Table; +import org.labkey.api.data.TableInfo; +import org.labkey.api.data.TableResultSet; +import org.labkey.api.data.TableSelector; +import org.labkey.api.data.TempTableInfo; +import org.labkey.api.data.UpdateableTableInfo; +import org.labkey.api.data.dialect.SqlDialect; +import org.labkey.api.dataiterator.DataIterator; +import org.labkey.api.dataiterator.DataIteratorBuilder; +import org.labkey.api.dataiterator.DataIteratorContext; +import org.labkey.api.dataiterator.DataIteratorUtil; +import org.labkey.api.dataiterator.ListofMapsDataIterator; +import org.labkey.api.dataiterator.LoggingDataIterator; +import org.labkey.api.dataiterator.MapDataIterator; +import org.labkey.api.dataiterator.Pump; +import org.labkey.api.dataiterator.SimpleTranslator; +import org.labkey.api.dataiterator.StandardDataIteratorBuilder; +import org.labkey.api.dataiterator.TableInsertDataIteratorBuilder; +import org.labkey.api.exceptions.OptimisticConflictException; +import org.labkey.api.exp.Lsid; +import org.labkey.api.exp.api.ExpSampleType; +import org.labkey.api.exp.api.ExperimentService; +import org.labkey.api.exp.api.SampleTypeService; +import org.labkey.api.exp.list.ListImportProgress; +import org.labkey.api.iterator.MarkableIterator; +import org.labkey.api.pipeline.PipelineJob; +import org.labkey.api.query.DefaultSchema; +import org.labkey.api.query.FieldKey; +import org.labkey.api.query.QueryUpdateService; +import org.labkey.api.query.ValidationException; +import org.labkey.api.reader.ColumnDescriptor; +import org.labkey.api.reader.DataLoader; +import org.labkey.api.reader.Readers; +import org.labkey.api.security.User; +import org.labkey.api.specimen.SpecimenEvent; +import org.labkey.api.specimen.SpecimenEventDateComparator; +import org.labkey.api.specimen.SpecimenEventManager; +import org.labkey.api.specimen.SpecimenSchema; +import org.labkey.api.specimen.Vial; +import org.labkey.api.specimen.importer.EventVialRollup; +import org.labkey.api.specimen.importer.RollupInstance; +import org.labkey.api.specimen.location.LocationCache; +import org.labkey.api.specimen.location.LocationManager; +import org.labkey.api.specimen.model.SpecimenComment; +import org.labkey.api.specimen.settings.SettingsManager; +import org.labkey.api.study.Location; +import org.labkey.api.study.SpecimenImportStrategy; +import org.labkey.api.study.SpecimenImportStrategyFactory; +import org.labkey.api.study.SpecimenService; +import org.labkey.api.study.Study; +import org.labkey.api.study.StudyInternalService; +import org.labkey.api.study.StudyService; +import org.labkey.api.study.StudyUtils; +import org.labkey.api.study.TimepointType; +import org.labkey.api.study.importer.ImportHelperService; +import org.labkey.api.study.importer.ImportHelperService.ParticipantIdTranslator; +import org.labkey.api.study.importer.ImportHelperService.SequenceNumTranslator; +import org.labkey.api.study.importer.SimpleStudyImportContext; +import org.labkey.api.study.model.VisitService; +import org.labkey.api.test.TestWhen; +import org.labkey.api.util.DateUtil; +import org.labkey.api.util.GUID; +import org.labkey.api.util.HeartBeat; +import org.labkey.api.util.JunitUtil; +import org.labkey.api.util.MultiPhaseCPUTimer; +import org.labkey.api.util.MultiPhaseCPUTimer.InvocationTimer.Order; +import org.labkey.api.util.Pair; +import org.labkey.api.util.ResultSetUtil; +import org.labkey.api.util.StringUtilsLabKey; +import org.labkey.api.util.TestContext; +import org.labkey.api.writer.VirtualFile; +import org.labkey.specimen.SpecimenColumns; +import org.labkey.specimen.SpecimenManager; +import org.labkey.specimen.SpecimenRequestManager; +import org.labkey.specimen.SpecimenTableManager; + +import java.io.BufferedReader; +import java.io.IOException; +import java.sql.SQLException; +import java.sql.Timestamp; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.TreeSet; +import java.util.concurrent.Callable; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +import static org.labkey.specimen.SpecimenColumns.DRAW_TIMESTAMP; +import static org.labkey.specimen.SpecimenColumns.GLOBAL_UNIQUE_ID; +import static org.labkey.specimen.SpecimenColumns.GLOBAL_UNIQUE_ID_TSV_COL; +import static org.labkey.specimen.SpecimenColumns.LAB_ID; +import static org.labkey.specimen.SpecimenColumns.LAB_RECEIPT_DATE; +import static org.labkey.specimen.SpecimenColumns.SHIP_DATE; +import static org.labkey.specimen.SpecimenColumns.SITE_COLUMNS; +import static org.labkey.specimen.SpecimenColumns.SPEC_NUMBER_TSV_COL; +import static org.labkey.specimen.SpecimenColumns.STORAGE_DATE; +import static org.labkey.specimen.SpecimenColumns.VISIT_COL; +import static org.labkey.specimen.SpecimenColumns.VISIT_VALUE; + +/** + * User: brittp + * Date: Mar 13, 2006 + * Time: 2:18:48 + */ +@SuppressWarnings({"ConstantConditions"}) +public class SpecimenImporter extends SpecimenTableManager +{ + private enum ImportPhases {UpdateCommentSpecimenHashes, MarkOrphanedRequestVials, SetLockedInRequest, VialUpdatePreLoopPrep, + GetVialBatch, GetDateOrderedEvents, GetSpecimenComments, GetProcessingLocationId, GetFirstProcessedBy, GetCurrentLocationId, + CalculateLocation, GetLastEvent, DetermineUpdateVial, SetUpdateParameters, HandleComments, UpdateVials, UpdateComments, + UpdateSpecimenProcessingInfo, UpdateRequestability, UpdateVialCounts, ResyncStudy, SetLastSpecimenLoad, DropTempTable, + UpdateAllStatistics, CommitTransaction, ClearCaches, PopulateMaterials, PopulateSpecimens, PopulateVials, PopulateSpecimenEvents, + PopulateTempTable, PopulateLabs, SpecimenTypes, DeleteOldData, PrepareQcComments, NotifyChanged} + + private static final MultiPhaseCPUTimer TIMER = new MultiPhaseCPUTimer<>(ImportPhases.class, ImportPhases.values()); + + private static class SpecimenLoadInfo + { + private final TempTableInfo _tempTableInfo; + private final String _tempTableName; + private final List _availableColumns; + private final int _rowCount; + private final Container _container; + private final User _user; + private final DbSchema _schema; + + public SpecimenLoadInfo(User user, Container container, DbSchema schema, List availableColumns, int rowCount, TempTableInfo tempTableInfo) + { + _user = user; + _schema = schema; + _container = container; + _availableColumns = availableColumns; + _rowCount = rowCount; + _tempTableName = tempTableInfo.getSelectName(); + _tempTableInfo = tempTableInfo; + } + + // Number of rows inserted into the temp table + public int getRowCount() + { + return _rowCount; + } + + public List getAvailableColumns() + { + return _availableColumns; + } + + public String getTempTableName() + { + return _tempTableName; + } + + public Container getContainer() + { + return _container; + } + + public User getUser() + { + return _user; + } + + public DbSchema getSchema() + { + return _schema; + } + } + + private static final String GENERAL_JOB_STATUS_MSG = "PROCESSING SPECIMENS"; + + private static final SpecimenColumn DRAW_DATE = new SpecimenColumn("", "DrawDate", "DATE", TargetTable.SPECIMENS); + private static final SpecimenColumn DRAW_TIME = new SpecimenColumn("", "DrawTime", "TIME", TargetTable.SPECIMENS); + + private List _specimenCols; + private List _vialCols; + private List _vialEventCols; + private String _specimenColsSql; + private String _vialColsSql; + private String _vialEventColsSql; + private Logger _logger; + private PipelineJob _job; + + protected int _generateGlobalUniqueIds = 0; + + private static final int SQL_BATCH_SIZE = 100; + + protected final SpecimenTableType _labsTableType; + protected final SpecimenTableType _additivesTableType; + protected final SpecimenTableType _derivativesTableType; + protected final SpecimenTableType _primaryTypesTableType; + protected final SpecimenTableType _specimensTableType; + + public @Nullable SpecimenTableType getForName(String name) + { + if (_labsTableType.getName().equalsIgnoreCase(name)) return _labsTableType; + if (_additivesTableType.getName().equalsIgnoreCase(name)) return _additivesTableType; + if (_derivativesTableType.getName().equalsIgnoreCase(name)) return _derivativesTableType; + if (_primaryTypesTableType.getName().equalsIgnoreCase(name)) return _primaryTypesTableType; + if ("specimens".equalsIgnoreCase(name)) return _specimensTableType; + return null; + } + + private MultiPhaseCPUTimer.InvocationTimer _iTimer; + + /** + * Constructor + * @param container import location + * @param user user whose permissions will be checked during import + */ + public SpecimenImporter(Container container, User user) + { + super(container, user); + + // The following could move to SpecimenTableManager, but they're only used by the importers + _specimensTableType = new SpecimenTableType("specimens", "study.Specimen", getSpecimenColumns()); + _labsTableType = new SpecimenTableType("labs", + SpecimenSchema.get().getTableInfoLocation(getContainer()).getSelectName(), SITE_COLUMNS); + _additivesTableType = new SpecimenTableType("additives", + SpecimenSchema.get().getTableInfoSpecimenAdditive(getContainer()).getSelectName(), SpecimenColumns.ADDITIVE_COLUMNS); + _derivativesTableType = new SpecimenTableType("derivatives", + SpecimenSchema.get().getTableInfoSpecimenDerivative(getContainer()).getSelectName(), SpecimenColumns.DERIVATIVE_COLUMNS); + _primaryTypesTableType = new SpecimenTableType("primary_types", + SpecimenSchema.get().getTableInfoSpecimenPrimaryType(getContainer()).getSelectName(), SpecimenColumns.PRIMARYTYPE_COLUMNS); + } + + private void resyncStudy(boolean syncParticipantVisit, boolean failForUndefinedVisits) throws ValidationException + { + TableInfo tableParticipant = SpecimenSchema.get().getTableInfoParticipant(); + TableInfo tableSpecimen = getTableInfoSpecimen(); + + executeSQL(tableParticipant.getSchema(), "INSERT INTO " + tableParticipant.getSelectName() + " (Container, ParticipantId)\n" + + "SELECT DISTINCT ?, ptid AS ParticipantId\n" + + "FROM " + tableSpecimen.getSelectName() + "\n" + + "WHERE ptid IS NOT NULL AND " + + "ptid NOT IN (SELECT ParticipantId FROM " + tableParticipant.getSelectName() + " WHERE Container = ?)", getContainer(), getContainer()); + + if (syncParticipantVisit) + { + Study study = StudyService.get().getStudy(getContainer()); + info("Updating study-wide subject/visit information..."); + ValidationException errors = VisitService.get().updateParticipantVisitsWithCohortUpdate(study, getUser(), failForUndefinedVisits, _logger); + if (errors.hasErrors()) + throw errors; + info("Subject/visit update complete."); + } + + info("Updating locations in use..."); + LocationManager.updateLocationTableInUse(getTableInfoLocation(), getContainer()); + } + + private void updateAllStatistics() + { + updateStatistics(ExperimentService.get().getTinfoMaterial()); + updateStatistics(getTableInfoSpecimen()); + updateStatistics(getTableInfoVial()); + updateStatistics(getTableInfoSpecimenEvent()); + } + + private boolean updateStatistics(TableInfo tinfo) + { + info("Updating statistics for " + tinfo + "..."); + boolean updated = tinfo.getSqlDialect().updateStatistics(tinfo); + if (updated) + info("Statistics update " + tinfo + " complete."); + else + info("Statistics update not supported for this database type."); + return updated; + } + + public void process(VirtualFile specimensDir, boolean merge, SimpleStudyImportContext ctx, @Nullable PipelineJob job, boolean syncParticipantVisit) + throws IOException, ValidationException + { + Map sifMap = populateFileMap(specimensDir, new HashMap<>()); + Study study = StudyService.get().getStudy(getContainer()); + process(sifMap, merge, ctx.getLogger(), job, syncParticipantVisit, false, ctx.isFailForUndefinedVisits() || study.isFailForUndefinedTimepoints()); + } + + protected void process(Map sifMap, boolean merge, Logger logger, @Nullable PipelineJob job, + boolean syncParticipantVisit, boolean editingSpecimens) + throws IOException, ValidationException + { + Study study = StudyService.get().getStudy(getContainer()); + process(sifMap, merge, logger, job, syncParticipantVisit, editingSpecimens, study.isFailForUndefinedTimepoints()); + } + + private void process(Map sifMap, boolean merge, Logger logger, @Nullable PipelineJob job, + boolean syncParticipantVisit, boolean editingSpecimens, boolean failForUndefinedVisits) + throws IOException, ValidationException + { + DbSchema schema = SpecimenSchema.get().getSchema(); + _logger = logger; + _job = job; + + DbScope scope = schema.getScope(); + boolean commitSuccessfully = false; + try (DbScope.Transaction transaction = scope.ensureTransaction()) + { + _iTimer = TIMER.getInvocationTimer(); + + setStatus(GENERAL_JOB_STATUS_MSG); + _iTimer.setPhase(ImportPhases.PopulateLabs); + if (null != sifMap.get(_labsTableType)) + { + try + { + mergeTable(schema, sifMap.get(_labsTableType), getTableInfoLocation(), true, true); + } + finally + { + LocationCache.clear(getContainer()); + } + } + + _iTimer.setPhase(ImportPhases.SpecimenTypes); + if (merge) + { + if (null != sifMap.get(_additivesTableType)) + mergeTable(schema, sifMap.get(_additivesTableType), getTableInfoAdditive(), false, true); + if (null != sifMap.get(_derivativesTableType)) + mergeTable(schema, sifMap.get(_derivativesTableType), getTableInfoDerivative(), false, true); + if (null != sifMap.get(_primaryTypesTableType)) + mergeTable(schema, sifMap.get(_primaryTypesTableType), getTableInfoPrimaryType(), false, true); + } + else + { + if (null != sifMap.get(_additivesTableType)) + replaceTable(schema, sifMap.get(_additivesTableType), getTableInfoAdditive(), false, true); + if (null != sifMap.get(_derivativesTableType)) + replaceTable(schema, sifMap.get(_derivativesTableType), getTableInfoDerivative(), false, true); + if (null != sifMap.get(_primaryTypesTableType)) + replaceTable(schema, sifMap.get(_primaryTypesTableType), getTableInfoPrimaryType(), false, true); + } + + // Specimen temp table must be populated AFTER the types tables have been reloaded, since the SpecimenHash + // calculated in the temp table relies on the new RowIds for the types: + setStatus(GENERAL_JOB_STATUS_MSG + " (temp table)"); + _iTimer.setPhase(ImportPhases.PopulateTempTable); + SpecimenImportFile specimenFile = sifMap.get(_specimensTableType); + SpecimenLoadInfo loadInfo = populateTempSpecimensTable(specimenFile, merge); + + // NOTE: if no rows were loaded in the temp table, don't remove existing materials/specimens/vials/events. + if (loadInfo.getRowCount() > 0) + populateSpecimenTables(loadInfo, merge); + else + info("Specimens: 0 rows found in input"); + + if (merge) + { + // Delete any orphaned specimen rows without vials + _iTimer.setPhase(ImportPhases.DeleteOldData); + executeSQL(SpecimenSchema.get().getSchema(), "DELETE FROM " + getTableInfoSpecimen().getSelectName() + + " WHERE RowId NOT IN (SELECT SpecimenId FROM " + getTableInfoVial().getSelectName() + ")"); + } + + // No need to setPhase() here... method sets timer phases immediately + updateCalculatedSpecimenData(merge, editingSpecimens); + + setStatus(GENERAL_JOB_STATUS_MSG + " (update study)"); + _iTimer.setPhase(ImportPhases.ResyncStudy); + resyncStudy(syncParticipantVisit, failForUndefinedVisits); + + ensureNotCanceled(); + _iTimer.setPhase(ImportPhases.SetLastSpecimenLoad); + // Set LastSpecimenLoad to now... we'll check this before snapshot study specimen refresh + StudyService.get().setLastSpecimenLoad( StudyService.get().getStudy(getContainer()), getUser(), new Date()); + + _iTimer.setPhase(ImportPhases.DropTempTable); + + // Drop the temp table within the transaction; otherwise, we may get a different connection object, + // where the table is no longer available. Note that this means that the temp table will stick around + // if an exception is thrown during loading, but this is probably okay- the DB will clean it up eventually. + loadInfo._tempTableInfo.delete(); + + _iTimer.setPhase(ImportPhases.UpdateAllStatistics); + updateAllStatistics(); + + // notify listeners that specimens have changed in this container + setStatus(GENERAL_JOB_STATUS_MSG); + _iTimer.setPhase(ImportPhases.NotifyChanged); + SpecimenService.get().fireSpecimensChanged(getContainer(), getUser(), _logger); + + _iTimer.setPhase(ImportPhases.CommitTransaction); + transaction.commit(); + commitSuccessfully = true; + } + finally + { + try + { + if (commitSuccessfully) + { + _iTimer.setPhase(ImportPhases.ClearCaches); + StudyInternalService.get().clearCaches(getContainer()); + SpecimenRequestManager.get().clearCaches(getContainer()); + + info(_iTimer.getTimings("Timings for each phase of this import are listed below:", Order.HighToLow, "|")); + } + } + finally + { + TIMER.releaseInvocationTimer(_iTimer); + } + } + } + + private SpecimenLoadInfo populateTempSpecimensTable(SpecimenImportFile file, boolean merge) throws IOException, ValidationException + { + TempTablesHolder tempTablesHolder = createTempTable(); + + Pair, Integer> pair = populateTempTable(tempTablesHolder, file, merge); + List columns = pair.first; + Integer rowCount = pair.second; + + tempTablesHolder.getCreateIndexes().run(); + + if (tempTablesHolder.getSelectInsertTempTableInfo().isTracking()) // If no specimens we will not have created this table + tempTablesHolder.getSelectInsertTempTableInfo().delete(); + + return new SpecimenLoadInfo(getUser(), getContainer(), DbSchema.getTemp(), columns, rowCount, tempTablesHolder.getTempTableInfo()); + } + + private void populateSpecimenTables(SpecimenLoadInfo info, boolean merge) throws ValidationException + { + setStatus(GENERAL_JOB_STATUS_MSG + " (populate tables)"); + _iTimer.setPhase(ImportPhases.DeleteOldData); + if (!merge) + { +// SimpleFilter containerFilter = SimpleFilter.createContainerFilter(info.getContainer()); + info("Deleting old data from SpecimenEvent, Vial and Specimen tables..."); + if (getTableInfoSpecimen().getSchema().getSqlDialect().isPostgreSQL()) + { + SQLFragment sql = new SQLFragment("TRUNCATE ") + .append(getTableInfoSpecimenEvent().getSelectName()) + .append(", ") + .append(getTableInfoVial().getSelectName()) + .append(", ") + .append(getTableInfoSpecimen().getSelectName()); + + executeSQL(getTableInfoSpecimen().getSchema(), sql); + } + else + { + Table.delete(getTableInfoSpecimenEvent()); + ensureNotCanceled(); + Table.delete(getTableInfoVial()); + ensureNotCanceled(); + Table.delete(getTableInfoSpecimen()); + } + info("Complete."); + } + + boolean seenVisitValue = false; + for (SpecimenColumn col : info.getAvailableColumns()) + { + if (VISIT_VALUE.getDbColumnName().equalsIgnoreCase(col.getDbColumnName())) + { + seenVisitValue = true; + break; + } + } + + ensureNotCanceled(); + _iTimer.setPhase(ImportPhases.PopulateMaterials); + populateMaterials(info, merge); + ensureNotCanceled(); + _iTimer.setPhase(ImportPhases.PopulateSpecimens); + populateSpecimens(info, merge, seenVisitValue); + ensureNotCanceled(); + _iTimer.setPhase(ImportPhases.PopulateVials); + populateVials(info, merge, seenVisitValue); + ensureNotCanceled(); + _iTimer.setPhase(ImportPhases.PopulateSpecimenEvents); + populateSpecimenEvents(info, merge); + } + + private Set getConflictingEventColumns(List events) + { + if (events.size() <= 1) + return Collections.emptySet(); + Set conflicts = new HashSet<>(); + + for (SpecimenColumn col : _specimenColumns) + { + if (col.getAggregateEventFunction() == null && col.getTargetTable() == TargetTable.SPECIMENS_AND_SPECIMEN_EVENTS) + { + // lower the case of the first character: + String propName = col.getDbColumnName().substring(0, 1).toLowerCase() + col.getDbColumnName().substring(1); + for (int i = 0; i < events.size() - 1; i++) + { + SpecimenEvent event = events.get(i); + SpecimenEvent nextEvent = events.get(i + 1); + Object currentValue = event.get(propName); + Object nextValue = nextEvent.get(propName); + if (!Objects.equals(currentValue, nextValue)) + { + if (propName.equalsIgnoreCase("drawtimestamp")) + { + Object currentDateOnlyValue = DateUtil.getDateOnly((Date) currentValue); + Object nextDateOnlyValue = DateUtil.getDateOnly((Date) nextValue); + if (!Objects.equals(currentDateOnlyValue, nextDateOnlyValue)) + conflicts.add(DRAW_DATE.getDbColumnName()); + Object currentTimeOnlyValue = DateUtil.getTimeOnly((Date) currentValue); + Object nextTimeOnlyValue = DateUtil.getTimeOnly((Date) nextValue); + if (!Objects.equals(currentTimeOnlyValue, nextTimeOnlyValue)) + conflicts.add(DRAW_TIME.getDbColumnName()); + } + else + { + conflicts.add(col.getDbColumnName()); + } + } + } + } + } + + return conflicts; + } + + private void clearConflictingVialColumns(Vial vial, Set conflicts) + { + SQLFragment sql = new SQLFragment() + .append("UPDATE ") + .append(getTableInfoVial().getSelectName()) + .append(" SET\n "); + + boolean hasConflict = false; + String sep = ""; + for (SpecimenColumn col : _specimenColumns) + { + if (col.getAggregateEventFunction() == null && col.getTargetTable().isVials() && !col.isUnique()) + { + if (conflicts.contains(col.getDbColumnName())) + { + hasConflict = true; + sql + .append(sep) + .append(col.getDbColumnName()).append(" = NULL"); + sep = ",\n "; + } + } + } + + if (!hasConflict) + return; + + sql + .append("\nWHERE GlobalUniqueId = ?") + .add(vial.getGlobalUniqueId()); + + new SqlExecutor(SpecimenSchema.get().getSchema()).execute(sql); + } + + private void updateCommentSpecimenHashes() + { + TableInfo commentTable = SpecimenSchema.get().getTableInfoSpecimenComment(); + String commentTableSelectName = commentTable.getSelectName(); + TableInfo vialTable = getTableInfoVial(); + String vialTableSelectName = vialTable.getSelectName(); + + SQLFragment sql = new SQLFragment() + .append("UPDATE ") + .append(commentTableSelectName) + .append(" SET SpecimenHash = (\n") + .append("SELECT SpecimenHash FROM ") + .append(vialTableSelectName).append(" WHERE ") + .append(vialTable.getColumn("GlobalUniqueId").getValueSql(vialTableSelectName)) + .append(" = ") + .append(commentTable.getColumn("GlobalUniqueId").getValueSql(commentTableSelectName)) + .append(")\nWHERE ") + .append(commentTable.getColumn("Container").getValueSql(commentTableSelectName)) + .append(" = ?") + .add(getContainer().getId()); + + info("Updating hash codes for existing comments..."); + new SqlExecutor(SpecimenSchema.get().getSchema()).execute(sql); + info("Complete."); + } + + private void prepareQCComments() + { + StringBuilder columnList = new StringBuilder(); + columnList.append("VialId"); + for (SpecimenColumn col : _specimenColumns) + { + if (col.getTargetTable() == TargetTable.SPECIMENS_AND_SPECIMEN_EVENTS && col.getAggregateEventFunction() == null) + { + columnList.append(",\n "); + columnList.append(col.getDbColumnName()); + } + } + + // find the global unique ID for those vials with conflicts: + TableInfo specimenEventTable = getTableInfoSpecimenEvent(); + SQLFragment conflictedGUIDs = new SQLFragment("SELECT GlobalUniqueId FROM ") + .append(getTableInfoVial(), "vial") + .append(" WHERE RowId IN (\n") + .append("SELECT VialId FROM\n") + .append("(SELECT DISTINCT\n") + .append(columnList).append("\nFROM ") + .append(specimenEventTable.getSelectName()) + .append("\nWHERE Obsolete = ") + .append(specimenEventTable.getSqlDialect().getBooleanFALSE()) + .append("\nGROUP BY\n") + .append(columnList) + .append(") ") + .append("AS DupCheckView\nGROUP BY VialId HAVING Count(VialId) > 1") + .append("\n)"); + + // Delete comments that were holding QC state (and nothing more) for vials that do not currently have any conflicts + SQLFragment deleteClearedVials = new SQLFragment("DELETE FROM study.SpecimenComment WHERE Container = ? ") + .add(getContainer().getId()) + .append("AND Comment IS NULL AND QualityControlFlag = ? ") + .add(Boolean.TRUE) + .append("AND QualityControlFlagForced = ? ") + .add(Boolean.FALSE) + .append("AND GlobalUniqueId NOT IN (") + .append(conflictedGUIDs) + .append(")"); + info("Clearing QC flags for vials that no longer have history conflicts..."); + new SqlExecutor(SpecimenSchema.get().getSchema()).execute(deleteClearedVials); + info("Complete."); + + + // Insert placeholder comments for newly discovered QC problems; SpecimenHash will be updated within updateCalculatedSpecimenData, so this + // doesn't have to be set here. + SQLFragment insertPlaceholderQCComments = new SQLFragment("INSERT INTO study.SpecimenComment ") + .append("(GlobalUniqueId, Container, QualityControlFlag, QualityControlFlagForced, Created, CreatedBy, Modified, ModifiedBy) ") + .append("SELECT GlobalUniqueId, ?, ?, ?, ?, ?, ?, ? ") + .add(getContainer().getId()) + .add(Boolean.TRUE) + .add(Boolean.FALSE) + .add(new Date()) + .add(getUser().getUserId()) + .add(new Date()) + .add(getUser().getUserId()) + .append(" FROM (\n").append(conflictedGUIDs).append(") ConflictedVials\n") + .append("WHERE GlobalUniqueId NOT IN ") + .append("(SELECT GlobalUniqueId FROM study.SpecimenComment WHERE Container = ?)") + .add(getContainer().getId()); + info("Setting QC flags for vials that have new history conflicts..."); + new SqlExecutor(SpecimenSchema.get().getSchema()).execute(insertPlaceholderQCComments); + info("Complete."); + } + + private void markOrphanedRequestVials() + { + // Mark those global unique IDs that are in requests but are no longer found in the vial table: + TableInfo vialTable = getTableInfoVial(); + String vialTableSelectName = vialTable.getSelectName(); + SQLFragment orphanMarkerSql = new SQLFragment() + .append("UPDATE study.SampleRequestSpecimen SET Orphaned = ? WHERE RowId IN (\n") + .add(Boolean.TRUE) + .append("\tSELECT study.SampleRequestSpecimen.RowId FROM study.SampleRequestSpecimen\n") + .append("\tLEFT OUTER JOIN ") + .append(vialTableSelectName) + .append(" ON\n\t\t") + .append(vialTable.getColumn("GlobalUniqueId").getValueSql(vialTableSelectName)) + .append(" = study.SampleRequestSpecimen.SpecimenGlobalUniqueId\n") + .append("\tWHERE ") + .append(vialTable.getColumn("GlobalUniqueId").getValueSql(vialTableSelectName)) + .append(" IS NULL AND\n") + .append("\t\tstudy.SampleRequestSpecimen.Container = ?)") + .add(getContainer().getId()); + info("Marking requested vials that have been orphaned..."); + + SqlExecutor executor = new SqlExecutor(SpecimenSchema.get().getSchema()); + executor.execute(orphanMarkerSql); + info("Complete."); + + // un-mark those global unique IDs that were previously marked as orphaned but are now found in the vial table: + SQLFragment deorphanMarkerSql = new SQLFragment() + .append("UPDATE study.SampleRequestSpecimen SET Orphaned = ? WHERE RowId IN (\n") + .add(Boolean.FALSE) + .append("\tSELECT study.SampleRequestSpecimen.RowId FROM study.SampleRequestSpecimen\n") + .append("\tLEFT OUTER JOIN ") + .append(vialTableSelectName) + .append(" ON\n\t\t") + .append(vialTable.getColumn("GlobalUniqueId").getValueSql(vialTableSelectName)) + .append(" = study.SampleRequestSpecimen.SpecimenGlobalUniqueId\n") + .append("\tWHERE ") + .append(vialTable.getColumn("GlobalUniqueId").getValueSql(vialTableSelectName)) + .append(" IS NOT NULL AND\n") + .append("\t\tstudy.SampleRequestSpecimen.Orphaned = ? AND\n") + .add(Boolean.TRUE) + .append("\t\tstudy.SampleRequestSpecimen.Container = ?)") + .add(getContainer().getId()); + info("Marking requested vials that have been de-orphaned..."); + executor.execute(deorphanMarkerSql); + info("Complete."); + } + + private void setLockedInRequestStatus() + { + TableInfo vialTable = getTableInfoVial(); + SqlDialect dialect = vialTable.getSqlDialect(); + String vialTableSelectName = vialTable.getSelectName(); + SQLFragment lockedInRequestSql = new SQLFragment("UPDATE ").append(vialTableSelectName).append( + " SET LockedInRequest = ").appendValue(Boolean.TRUE, dialect).append(" WHERE RowId IN (SELECT ").append(vialTable.getColumn("RowId").getValueSql(vialTableSelectName)) + .append(" FROM ").append(vialTableSelectName).append(", study.LockedSpecimens " + + "WHERE study.LockedSpecimens.Container = ").appendValue(getContainer(), dialect).append(" AND ") + .append(vialTable.getColumn("GlobalUniqueId").getValueSql(vialTableSelectName)).append(" = study.LockedSpecimens.GlobalUniqueId)"); + + info("Setting Specimen Locked in Request status..."); + new SqlExecutor(SpecimenSchema.get().getSchema()).execute(lockedInRequestSql); + info("Complete."); + } + + private void updateSpecimenProcessingInfo() + { + TableInfo specimenTable = getTableInfoSpecimen(); + String specimenTableSelectName = specimenTable.getSelectName(); + TableInfo vialTable = getTableInfoVial(); + String vialTableSelectName = vialTable.getSelectName(); + SQLFragment sql = new SQLFragment("UPDATE ").append(specimenTableSelectName).append(" SET ProcessingLocation = (\n" + + "\tSELECT MAX(ProcessingLocation) AS ProcessingLocation FROM \n" + + "\t\t(SELECT DISTINCT SpecimenId, ProcessingLocation FROM ").append(vialTableSelectName).append( + " WHERE SpecimenId = ").append(specimenTable.getColumn("RowId").getValueSql(specimenTableSelectName)).append(") Locations\n" + + "\tGROUP BY SpecimenId\n" + + "\tHAVING COUNT(ProcessingLocation) = 1\n" + + ")"); + info("Updating processing locations on the specimen table..."); + new SqlExecutor(SpecimenSchema.get().getSchema()).execute(sql); + info("Complete."); + + sql = new SQLFragment("UPDATE ").append(specimenTableSelectName).append(" SET FirstProcessedByInitials = (\n" + + "\tSELECT MAX(FirstProcessedByInitials) AS FirstProcessedByInitials FROM \n" + + "\t\t(SELECT DISTINCT SpecimenId, FirstProcessedByInitials FROM ").append(vialTableSelectName).append( + " WHERE SpecimenId = ").append(specimenTable.getColumn("RowId").getValueSql(specimenTableSelectName)).append(") Locations\n" + + "\tGROUP BY SpecimenId\n" + + "\tHAVING COUNT(FirstProcessedByInitials) = 1\n" + + ")"); + info("Updating first processed by initials on the specimen table..."); + new SqlExecutor(SpecimenSchema.get().getSchema()).execute(sql); + info("Complete."); + } + + private static final int CURRENT_SITE_UPDATE_SIZE = 1000; + private static final int CURRENT_SITE_UPDATE_LOGGING_SIZE = 10000; // Can choose to log at a less frequent rate than the update batch size + + // UNDONE: add vials in-clause to only update data for rows that changed + private void updateCalculatedSpecimenData(final boolean merge, final boolean editingSpecimens) + { + setStatus(GENERAL_JOB_STATUS_MSG + " (update)"); + _iTimer.setPhase(ImportPhases.PrepareQcComments); + // delete unnecessary comments and create placeholders for newly discovered errors: + prepareQCComments(); + + _iTimer.setPhase(ImportPhases.UpdateCommentSpecimenHashes); + updateCommentSpecimenHashes(); + + _iTimer.setPhase(ImportPhases.MarkOrphanedRequestVials); + markOrphanedRequestVials(); + _iTimer.setPhase(ImportPhases.SetLockedInRequest); + setLockedInRequestStatus(); + + _iTimer.setPhase(ImportPhases.VialUpdatePreLoopPrep); + // clear caches before determining current sites: + SpecimenRequestManager.get().clearCaches(getContainer()); + final Map siteMap = new HashMap<>(); + + TableInfo vialTable = getTableInfoVial(); + StringBuilder vialPropertiesSB = new StringBuilder("UPDATE ").append(vialTable.getSelectName()) + .append(" SET CurrentLocation = CAST(? AS INTEGER), ProcessingLocation = CAST(? AS INTEGER), FirstProcessedByInitials = ?, AtRepository = ?, LatestComments = ?, LatestQualityComments = ? "); + + for (List> rollupList : getEventToVialRollups().values()) + { + for (RollupInstance rollup : rollupList) + { + String colName = rollup.first; + ColumnInfo column = vialTable.getColumn(colName); + if (null == column) + throw new IllegalStateException("Expected Vial table column to exist."); + vialPropertiesSB.append(", ").append(column.getSelectName()).append(" = ") + .append(JdbcType.VARCHAR.equals(column.getJdbcType()) ? "?" : "CAST(? AS " + vialTable.getSqlDialect().getSqlCastTypeName(column.getJdbcType()) + ")"); + } + } + + vialPropertiesSB.append(" WHERE RowId = ?"); + + final String vialPropertiesSql = vialPropertiesSB.toString(); + + _iTimer.setPhase(ImportPhases.HandleComments); + + TableInfo commentTable = SpecimenSchema.get().getTableInfoSpecimenComment(); + final String updateCommentSql = "UPDATE " + commentTable + " SET QualityControlComments = ? WHERE GlobalUniqueId = ?"; + + // Populate a GlobalUniqueId -> SpecimenEvent map containing all quality control vial comments in this container + final Map qcCommentMap = new HashMap<>(); + + SQLFragment selectCommentsSql = new SQLFragment() + .append("SELECT c.* FROM ") + .append(commentTable, "c") + .append(" INNER JOIN ") + .append(getTableInfoVial(), "v") + .append(" ON c.GlobalUniqueId = v.GlobalUniqueId WHERE Container = ? AND (QualityControlFlag = ? OR QualityControlFlagForced = ?)") + .add(getContainer()) + .add(true) + .add(true); + + new SqlSelector(SpecimenSchema.get().getSchema(), selectCommentsSql).forEach(SpecimenComment.class, comment -> qcCommentMap.put(comment.getGlobalUniqueId(), comment)); + +// if (!merge) +// new SpecimenTablesProvider(getContainer(), getUser(), null).dropTableIndices(SpecimenTablesProvider.VIAL_TABLENAME); + + // TODO: Select only required subset of Event and Vial columns? + _iTimer.setPhase(ImportPhases.GetDateOrderedEvents); + + TableSelector eventSelector = new TableSelector(getTableInfoSpecimenEvent(), new SimpleFilter(FieldKey.fromString("Obsolete"), false), new Sort("VialId")); + + try (Results eventResults = eventSelector.getResults(false)) + { + final MutableInt rowCount = new MutableInt(); + final MarkableIterator> eventIterator = new MarkableIterator<>(eventResults.iterator()); + + _iTimer.setPhase(ImportPhases.GetVialBatch); + TableSelector vialSelector = new TableSelector(getTableInfoVial(), null, new Sort("RowId")); + + vialSelector.forEachMapBatch(CURRENT_SITE_UPDATE_SIZE, vialBatch -> { + int count = rowCount.intValue(); + if (count % CURRENT_SITE_UPDATE_LOGGING_SIZE == 0) + info("Updating vial rows " + (count + 1) + " through " + (count + CURRENT_SITE_UPDATE_LOGGING_SIZE) + "."); + + setStatus(GENERAL_JOB_STATUS_MSG + " (update vials)"); + + final List vials = new ArrayList<>(CURRENT_SITE_UPDATE_SIZE); + + for (Map map : vialBatch) + vials.add(new Vial(getContainer(), map)); + + List> vialPropertiesParams = new ArrayList<>(CURRENT_SITE_UPDATE_SIZE); + List> commentParams = new ArrayList<>(); + + for (Vial vial : vials) + { + long vialId = vial.getRowId(); + + _iTimer.setPhase(ImportPhases.GetDateOrderedEvents); + List dateOrderedEvents = new ArrayList<>(); + while (eventIterator.hasNext()) + { + eventIterator.mark(); + Map map = eventIterator.next(); + + if (vialId == (Long) map.get("VialId")) + { + dateOrderedEvents.add(new SpecimenEvent(getContainer(), map)); + } + else + { + eventIterator.reset(); + break; + } + } + dateOrderedEvents.sort(SpecimenEventDateComparator.get()); + + _iTimer.setPhase(ImportPhases.GetProcessingLocationId); + Integer processingLocation = LocationManager.get().getProcessingLocationId(dateOrderedEvents); + _iTimer.setPhase(ImportPhases.GetFirstProcessedBy); + String firstProcessedByInitials = SpecimenManager.get().getFirstProcessedByInitials(dateOrderedEvents); + _iTimer.setPhase(ImportPhases.GetCurrentLocationId); + Integer currentLocation = LocationManager.get().getCurrentLocationId(dateOrderedEvents); + + _iTimer.setPhase(ImportPhases.CalculateLocation); + boolean atRepository = false; + + if (currentLocation != null) + { + Location location; + + if (!siteMap.containsKey(currentLocation)) + { + location = LocationManager.get().getLocation(getContainer(), currentLocation); + if (location != null) + siteMap.put(currentLocation, location); + } + else + { + location = siteMap.get(currentLocation); + } + + if (location != null) + atRepository = location.isRepository() != null && location.isRepository(); + } + + // All of the additional fields (deviationCodes, Concentration, Integrity, Yield, Ratio, QualityComments, Comments) always take the latest value + _iTimer.setPhase(ImportPhases.GetLastEvent); + SpecimenEvent lastEvent = SpecimenEventManager.get().getLastEvent(dateOrderedEvents); + if (null == lastEvent) + throw new IllegalStateException("There should always be at least 1 event."); + + _iTimer.setPhase(ImportPhases.DetermineUpdateVial); + boolean updateVial = false; + List params = new ArrayList<>(); + + if (!Objects.equals(currentLocation, vial.getCurrentLocation()) || + !Objects.equals(processingLocation, vial.getProcessingLocation()) || + !Objects.equals(firstProcessedByInitials, vial.getFirstProcessedByInitials()) || + atRepository != vial.isAtRepository() || + !Objects.equals(vial.getLatestComments(), lastEvent.getComments()) || + !Objects.equals(vial.getLatestQualityComments(), lastEvent.getQualityComments())) + { + updateVial = true; // Something is different + } + + if (!updateVial) + { + for (Map.Entry>> rollupEntry : getEventToVialRollups().entrySet()) + { + String eventColName = rollupEntry.getKey(); + ColumnInfo column = getTableInfoSpecimenEvent().getColumn(eventColName); + if (null == column) + throw new IllegalStateException("Expected Specimen Event table column to exist."); + String eventColSelectName = column.getSelectName(); + for (RollupInstance rollupItem : rollupEntry.getValue()) + { + String vialColName = rollupItem.first; + Object rollupResult = rollupItem.second.getRollupResult(dateOrderedEvents, eventColSelectName, + rollupItem.getFromType(), rollupItem.getToType()); + if (!Objects.equals(vial.get(vialColName), rollupResult)) + { + updateVial = true; // Something is different + break; + } + } + if (updateVial) + break; + } + } + + _iTimer.setPhase(ImportPhases.SetUpdateParameters); + if (updateVial) + { + // Something is different; update everything + params.add(currentLocation); + params.add(processingLocation); + params.add(firstProcessedByInitials); + params.add(atRepository); + params.add(lastEvent.getComments()); + params.add(lastEvent.getQualityComments()); + + for (Map.Entry>> rollupEntry : getEventToVialRollups().entrySet()) + { + String eventColName = rollupEntry.getKey(); + ColumnInfo column = getTableInfoSpecimenEvent().getColumn(eventColName); + if (null == column) + throw new IllegalStateException("Expected Specimen Event table column to exist."); + String eventColAlias = column.getAlias(); // Use alias since we're looking up in the rowMap + for (RollupInstance rollupItem : rollupEntry.getValue()) + { + Object rollupResult = rollupItem.second.getRollupResult(dateOrderedEvents, eventColAlias, + rollupItem.getFromType(), rollupItem.getToType()); + params.add(rollupResult); + } + } + + params.add(vial.getRowId()); + vialPropertiesParams.add(params); + } + + _iTimer.setPhase(ImportPhases.HandleComments); + SpecimenComment comment = qcCommentMap.get(vial.getGlobalUniqueId()); + + if (comment != null) + { + // if we have a comment, it may be because we're in a bad QC state. If so, we should update + // the reason for the QC problem. + String message = null; + + Set conflicts = getConflictingEventColumns(dateOrderedEvents); + + if (!conflicts.isEmpty()) + { + // Null out conflicting Vial columns + if (merge) + { + // NOTE: in checkForConflictingSpecimens() we check the imported specimen columns used + // to generate the specimen hash are not in conflict so we shouldn't need to clear any + // columns on the specimen table. Vial columns are not part of the specimen hash and + // can safely be cleared without compromising the specimen hash. + clearConflictingVialColumns(vial, conflicts); + } + + String sep = ""; + message = "Conflicts found: "; + for (String conflict : conflicts) + { + message += sep + conflict; + sep = ", "; + } + } + + commentParams.add(Arrays.asList(message, vial.getGlobalUniqueId())); + } + } + + _iTimer.setPhase(ImportPhases.UpdateVials); + if (!vialPropertiesParams.isEmpty()) + Table.batchExecute(SpecimenSchema.get().getSchema(), vialPropertiesSql, vialPropertiesParams); + + _iTimer.setPhase(ImportPhases.UpdateComments); + if (!commentParams.isEmpty()) + Table.batchExecute(SpecimenSchema.get().getSchema(), updateCommentSql, commentParams); + + rowCount.add(CURRENT_SITE_UPDATE_SIZE); + _iTimer.setPhase(ImportPhases.GetVialBatch); + }); + } + catch (SQLException e) + { + throw new RuntimeSQLException(e); + } + +// if (!merge) +// new SpecimenTablesProvider(getContainer(), getUser(), null).addTableIndices(SpecimenTablesProvider.VIAL_TABLENAME); + + // finally, after all other data has been updated, we can update our cached specimen counts and processing locations: + setStatus(GENERAL_JOB_STATUS_MSG + " (update counts)"); + _iTimer.setPhase(ImportPhases.UpdateSpecimenProcessingInfo); + updateSpecimenProcessingInfo(); + + _iTimer.setPhase(ImportPhases.UpdateRequestability); + try + { + RequestabilityManager.getInstance().updateRequestability(getContainer(), getUser(), false, editingSpecimens, _logger); + } + catch (RequestabilityManager.InvalidRuleException e) + { + throw new IllegalStateException("One or more requestability rules is invalid. Please remove or correct the invalid rule.", e); + } + + _iTimer.setPhase(ImportPhases.UpdateVialCounts); + info("Updating cached vial counts..."); + + SpecimenRequestManager.get().updateVialCounts(getContainer(), getUser()); + + info("Vial count update complete."); + } + + private Map populateFileMap(VirtualFile dir, Map fileNameMap) throws IOException + { + for (String dirName : dir.listDirs()) + { + populateFileMap(dir.getDir(dirName), fileNameMap); + } + + for (String fileName : dir.list()) + { + if (!fileName.toLowerCase().endsWith(".tsv")) + continue; + + try (BufferedReader reader = Readers.getReader(dir.getInputStream(fileName))) + { + String line = reader.readLine(); + if (null == line) + continue; + line = StringUtils.trimToEmpty(line); + if (!line.startsWith("#")) + throw new IllegalStateException("Import files are expected to start with a comment indicating table name"); + + String canonicalName = line.substring(1).trim().toLowerCase(); + SpecimenTableType type = getForName(canonicalName); + + if (null != type) + fileNameMap.put(type, getSpecimenImportFile(getContainer(), dir, fileName, type)); + } + } + + return fileNameMap; + } + + // TODO: Pass in merge (or import strategy)? + private SpecimenImportFile getSpecimenImportFile(Container c, VirtualFile dir, String fileName, SpecimenTableType type) + { + DbSchema schema = SpecimenSchema.get().getSchema(); + + // Enumerate the import filter factories... first one to claim the file gets associated with it + for (SpecimenImportStrategyFactory factory : SpecimenService.get().getSpecimenImportStrategyFactories()) + { + SpecimenImportStrategy strategy = factory.get(schema, c, dir, fileName); + + if (null != strategy) + return new FileSystemSpecimenImportFile(dir, fileName, strategy, type); + } + + throw new IllegalStateException("No SpecimenImportStrategyFactory claimed this import!"); + } + + private void info(String message) + { + if (_logger != null) + _logger.info(message); + } + + private void debug(CharSequence message) + { + //noinspection PointlessBooleanExpression + if (DEBUG && _logger != null) + _logger.debug(message); + } + + private static String _currentStatus = GENERAL_JOB_STATUS_MSG; + private void setStatus(@Nullable String status) + { + if (null != _job) + { + _currentStatus = status; + _job.setStatus(_currentStatus); + } + } + + private void ensureNotCanceled() + { + if (null != _job) + _job.setStatus(_currentStatus); // Will throw if job has been canceled + } + + private List getSpecimenCols(List availableColumns) + { + if (_specimenCols == null) + { + List cols = new ArrayList<>(availableColumns.size()); + for (SpecimenColumn col : availableColumns) + { + if (col.getTargetTable().isSpecimens()) + cols.add(col); + } + _specimenCols = cols; + } + return _specimenCols; + } + + private String getSpecimenColsSql(List availableColumns, boolean seenVisitValue) + { + if (_specimenColsSql == null) + { + String sep = ""; + StringBuilder cols = new StringBuilder(); + for (SpecimenColumn col : getSpecimenCols(availableColumns)) + { + cols.append(sep).append(col.getLegalDbColumnName(getSqlDialect())); + sep = ",\n "; + } + if (!seenVisitValue) + cols.append(sep).append(VISIT_VALUE.getDbColumnName()); + _specimenColsSql = cols.toString(); + } + return _specimenColsSql; + } + + private List getVialCols(List availableColumns) + { + if (_vialCols == null) + { + List cols = new ArrayList<>(availableColumns.size()); + for (SpecimenColumn col : availableColumns) + { + if (col.getTargetTable().isVials()) + cols.add(col); + } + _vialCols = cols; + } + return _vialCols; + } + + private String getVialColsSql(List availableColumns) + { + if (_vialColsSql == null) + { + String sep = ""; + StringBuilder cols = new StringBuilder(); + for (SpecimenColumn col : getVialCols(availableColumns)) + { + cols.append(sep).append(col.getLegalDbColumnName(getSqlDialect())); + sep = ",\n "; + } + _vialColsSql = cols.toString(); + } + return _vialColsSql; + } + + private List getSpecimenEventCols(List availableColumns) + { + if (_vialEventCols == null) + { + List cols = new ArrayList<>(availableColumns.size()); + for (SpecimenColumn col : availableColumns) + { + if (col.getTargetTable().isEvents()) + cols.add(col); + } + _vialEventCols = cols; + } + return _vialEventCols; + } + + private String getSpecimenEventColsSql(List availableColumns) + { + if (_vialEventColsSql == null) + { + String sep = ""; + StringBuilder cols = new StringBuilder(); + for (SpecimenColumn col : getSpecimenEventCols(availableColumns)) + { + cols.append(sep).append(col.getLegalDbColumnName(getSqlDialect())); + sep = ",\n "; + } + _vialEventColsSql = cols.toString(); + } + return _vialEventColsSql; + } + + private void populateMaterials(SpecimenLoadInfo info, boolean merge) + { + String columnName = null; + + for (SpecimenColumn specimenColumn : info.getAvailableColumns()) + { + if (GLOBAL_UNIQUE_ID_TSV_COL.equals(specimenColumn.getPrimaryTsvColumnName())) + { + columnName = specimenColumn.getDbColumnName(); + break; + } + } + + if (columnName == null) + { + for (SpecimenColumn specimenColumn : info.getAvailableColumns()) + { + if (SPEC_NUMBER_TSV_COL.equals(specimenColumn.getPrimaryTsvColumnName())) + { + columnName = specimenColumn.getDbColumnName(); + break; + } + } + } + + if (columnName == null) + { + throw new IllegalStateException("Could not find a unique specimen identifier column. Either \"" + GLOBAL_UNIQUE_ID_TSV_COL + + "\" or \"" + SPEC_NUMBER_TSV_COL + "\" must be present in the set of specimen columns."); + } + + String insertObjectSQL = "INSERT INTO exp.Object (ObjectURI, Container) \n" + + "SELECT DISTINCT T.LSID AS ObjectURI, ? AS Container\n" + + "FROM " + info.getTempTableName() + " T LEFT OUTER JOIN exp.Object O ON T.LSID = O.ObjectURI\n" + + "WHERE O.ObjectURI IS NULL\n"; + + String countMaterialToInsertSQL = "SELECT DISTINCT T.LSID\n" + + "FROM " + info.getTempTableName() + " T LEFT OUTER JOIN exp.Material M ON T.LSID = M.LSID\n" + + "WHERE M.LSID IS NULL\n"; + + String insertMaterialSQL = "INSERT INTO exp.Material (RowId, LSID, rootMaterialRowId, Name, ObjectId, Container, CpasType, Created) \n" + + "SELECT ? + (ROW_NUMBER() OVER (ORDER BY ObjectId)), LSID, ? + (ROW_NUMBER() OVER (ORDER BY ObjectId)), Name, ObjectId, Container, CpasType, Created FROM\n" + + "(SELECT DISTINCT T.LSID, T." + columnName + " AS Name, (SELECT ObjectId FROM exp.Object O where O.ObjectURI=T.LSID) AS ObjectId, ? AS Container, ? AS CpasType, CAST(? AS " + getSqlDialect().getDefaultDateTimeDataType()+ ") AS Created\n" + + " FROM " + info.getTempTableName() + " T LEFT OUTER JOIN exp.Material M ON T.LSID = M.LSID\n" + + " WHERE M.LSID IS NULL) X\n"; + + /* NOTE: Not really necessary to delete and recreate the object rows + String deleteObjectSQL = "DELETE FROM exp.Object WHERE ObjectURI IN (SELECT M.LSID FROM exp.Material M\n" + + "LEFT OUTER JOIN " + info.getTempTableName() + " T ON M.LSID = T.LSID\n" + + "LEFT OUTER JOIN exp.MaterialInput MI ON M.RowId = MI.MaterialId\n" + + "WHERE T.LSID IS NULL\n" + + "AND MI.MaterialId IS NULL\n" + + "AND M.CpasType IN (?, '" + StudyService.SPECIMEN_NAMESPACE_PREFIX + "') \n" + + "AND M.Container = ?)"; + */ + + String deleteMaterialSQL = "DELETE FROM exp.Material WHERE RowId IN (SELECT M.RowId FROM exp.Material M\n" + + "LEFT OUTER JOIN " + info.getTempTableName() + " T ON M.LSID = T.LSID\n" + + "LEFT OUTER JOIN exp.MaterialInput MI ON M.RowId = MI.MaterialId\n" + + "WHERE T.LSID IS NULL\n" + + "AND MI.MaterialId IS NULL\n" + + "AND M.CpasType IN (?, '" + StudyService.SPECIMEN_NAMESPACE_PREFIX + "') \n" + + "AND M.Container = ?)"; + + String prefix = new Lsid(StudyService.SPECIMEN_NAMESPACE_PREFIX, "Folder-" + info.getContainer().getRowId(), "").toString(); + String cpasType; + ExpSampleType sampleType = SampleTypeService.get().getSampleType(info.getContainer(), SpecimenService.SAMPLE_TYPE_NAME); + + if (sampleType == null) + { + ExpSampleType source = SampleTypeService.get().createSampleType(); + source.setContainer(info.getContainer()); + source.setMaterialLSIDPrefix(prefix); + source.setName(SpecimenService.SAMPLE_TYPE_NAME); + source.setLSID(SampleTypeService.get().getSampleTypeLsid(SpecimenService.SAMPLE_TYPE_NAME, info.getContainer()).toString()); + source.setDescription("Study specimens for " + info.getContainer().getPath()); + source.save(null); + cpasType = source.getLSID(); + } + else + { + cpasType = sampleType.getLSID(); + } + + var createdTimestamp = new Parameter.TypedValue(new Timestamp(System.currentTimeMillis()), JdbcType.TIMESTAMP); + + int affected; + if (!merge) + { + info("exp.Material: Deleting entries for removed specimens..."); + SQLFragment deleteFragment = new SQLFragment(deleteMaterialSQL, cpasType, info.getContainer().getId()); + if (DEBUG) + logSQLFragment(deleteFragment); + affected = executeSQL(info.getSchema(), deleteFragment); + if (affected >= 0) + info("exp.Material: " + affected + " rows removed."); + } + + // NOTE: No need to update existing Materials when merging -- just insert any new materials not found. + info("exp.Material: Inserting new entries from temp table..."); + SQLFragment insertObjectFragment = new SQLFragment(insertObjectSQL, info.getContainer().getId()); + if (DEBUG) + logSQLFragment(insertObjectFragment); + executeSQL(info.getSchema(), insertObjectFragment); + + long count = new SqlSelector(info.getSchema(), countMaterialToInsertSQL).getRowCount(); + if (count > 0) + { + // reserve rowids for this insert + // NOTE QuerySchema exp.Materials (plural) has dbsequence info, but DbSchema exp.Material (singular) does not + TableInfo material = DefaultSchema.get(info.getUser(), info.getContainer(),"exp").getTable("Materials", null); + ColumnInfo rowId = material.getColumn("RowId"); + DbSequence seq = DbSequenceManager.getPreallocatingSequence(rowId.getDbSequenceContainer(null), material.getDbSequenceName(rowId.getName()), 0, 1000); + + /* HACK, we don't do this anywhere else, but we need to get a block of continuous IDS to make this work */ + // row_number() starts at 1 so subtract 1 + long start = DbSequenceManager.reserveSequentialBlock(seq, (int)count); + start -= 1; + + SQLFragment insertMaterialFragment = new SQLFragment(insertMaterialSQL, start, start, info.getContainer().getId(), cpasType, createdTimestamp); + if (DEBUG) + logSQLFragment(insertMaterialFragment); + affected = executeSQL(info.getSchema(), insertMaterialFragment); + if (affected >= 0) + info("exp.Material: " + affected + " rows inserted."); + } + + info("exp.Material: Update complete."); + } + + private String getSpecimenEventTempTableColumns(SpecimenLoadInfo info) + { + StringBuilder columnList = new StringBuilder(); + String prefix = ""; + for (SpecimenColumn col : getSpecimenEventCols(info.getAvailableColumns())) + { + columnList.append(prefix); + prefix = ", "; + columnList.append("\n ").append(info.getTempTableName()).append(".").append(col.getLegalDbColumnName(getSqlDialect())); + } + return columnList.toString(); + } + + // NOTE: In merge case, we've already checked the specimen hash columns are not in conflict. + private void appendConflictResolvingSQL(SqlDialect dialect, SQLFragment sql, SpecimenColumn col, String tempTableName, + @Nullable SpecimenColumn castColumn) + { + // If castColumn no null, then we still count col, but then cast col's value to castColumn's type and name it castColumn's name + String selectCol = tempTableName + "." + col.getLegalDbColumnName(getSqlDialect()); + + if (col.getAggregateEventFunction() != null) + { + sql.append(col.getAggregateEventFunction()).append("(").append(selectCol).append(")"); + } + else + { + sql.append("CASE WHEN"); + if (col.getJavaClass().equals(Boolean.class)) + { + // gross nested calls to cast the boolean to an int, get its min, then cast back to a boolean. + // this is needed because most aggregates don't work on boolean values. + sql.append(" COUNT(DISTINCT ").append(selectCol).append(") = 1 THEN "); + sql.append("CAST(MIN(CAST(").append(selectCol).append(" AS INTEGER)) AS ").append(dialect.getBooleanDataType()).append(")"); + } + else + { + if (null != castColumn) + { + sql.append(" COUNT(DISTINCT ").append(tempTableName).append(".").append(castColumn.getLegalDbColumnName(getSqlDialect())).append(") = 1 THEN "); + sql.append("CAST(MIN(").append(selectCol).append(") AS ").append(castColumn.getDbType()).append(")"); + } + else + { + sql.append(" COUNT(DISTINCT ").append(selectCol).append(") = 1 THEN "); + sql.append("MIN(").append(selectCol).append(")"); + } + } + sql.append(" ELSE NULL END"); + } + sql.append(" AS "); + + if (null != castColumn) + sql.append(castColumn.getLegalDbColumnName(getSqlDialect())); + else + sql.append(col.getLegalDbColumnName(getSqlDialect())); + } + + + private void populateSpecimens(SpecimenLoadInfo info, boolean merge, boolean seenVisitValue) throws ValidationException + { + String participantSequenceNumExpr = StudyUtils.getParticipantSequenceNumExpr(info._schema, "PTID", "VisitValue"); + + SQLFragment insertSelectSql = new SQLFragment() + .append("SELECT ") + .append(participantSequenceNumExpr) + .append(" AS ParticipantSequenceNum") + .append(", SpecimenHash, ") + .append(DRAW_DATE.getDbColumnName()) + .append(", ") + .append(DRAW_TIME.getDbColumnName()) + .append(", ") + .append(getSpecimenColsSql(info.getAvailableColumns(), seenVisitValue)) + .append(" FROM (\n") + .append(getVialListFromTempTableSql(info, true, seenVisitValue)) + .append(") VialList\n") + .append("GROUP BY ") + .append("SpecimenHash, ") + .append(getSpecimenColsSql(info.getAvailableColumns(), seenVisitValue)) + .append(", ") + .append(DRAW_DATE.getDbColumnName()) + .append(", ") + .append(DRAW_TIME.getDbColumnName()); + + TableInfo specimenTable = getTableInfoSpecimen(); + String specimenTableSelectName = specimenTable.getSelectName(); + if (merge) + { + // Create list of specimen columns, including unique columns not found in SPECIMEN_COLUMNS. + Set cols = new LinkedHashSet<>(); + cols.add(new SpecimenColumn("SpecimenHash", "SpecimenHash", "VARCHAR(256)", TargetTable.SPECIMENS, true)); + cols.add(new SpecimenColumn("ParticipantSequenceNum", "ParticipantSequenceNum", "VARCHAR(200)", TargetTable.SPECIMENS, false)); + cols.add(DRAW_DATE); + cols.add(DRAW_TIME); + cols.addAll(getSpecimenCols(info.getAvailableColumns())); + + // Insert or update the specimens from in the temp table + try (TableResultSet rs = new SqlSelector(info.getSchema(), insertSelectSql).getResultSet()) + { + if (VERBOSE_DEBUG) + ResultSetUtil.logData(rs, _logger); + DataIteratorBuilder dib = new DataIteratorBuilder.Wrapper(new ResultsImpl(rs)); + mergeTable(info.getSchema(), specimenTableSelectName, specimenTable, cols, dib, false, false); + } + catch (SQLException e) + { + throw new RuntimeSQLException(e); + } + } + else + { + // Insert all specimens from in the temp table. + SQLFragment insertSql = new SQLFragment() + .append("INSERT INTO ") + .append(specimenTableSelectName) + .append("\n(") + .append("ParticipantSequenceNum, SpecimenHash, ") + .append(DRAW_DATE.getDbColumnName()) + .append(", ") + .append(DRAW_TIME.getDbColumnName()) + .append(", ") + .append(getSpecimenColsSql(info.getAvailableColumns(), seenVisitValue)) + .append(")\n") + .append(insertSelectSql); + + if (DEBUG) + logSQLFragment(insertSql); + + info("Specimens: Inserting new rows..."); + executeSQL(info.getSchema(), insertSql); + info("Specimens: Insert complete."); + } + } + + private SQLFragment getVialListFromTempTableSql(SpecimenLoadInfo info, boolean forSpecimenTable, boolean seenVisitValue) + { + String prefix = ""; + SQLFragment vialListSql = new SQLFragment() + .append("SELECT "); + if (!forSpecimenTable) + { + vialListSql + .append(info.getTempTableName()) + .append(".LSID AS LSID"); + prefix = ",\n "; + } + vialListSql + .append(prefix) + .append("SpecimenHash"); + prefix = ",\n "; + for (SpecimenColumn col : info.getAvailableColumns()) + { + if ((col.getTargetTable().isVials() || col.getTargetTable().isSpecimens()) && + (!forSpecimenTable || !GLOBAL_UNIQUE_ID.getDbColumnName().equalsIgnoreCase(col.getDbColumnName()))) + { + vialListSql.append(prefix); + appendConflictResolvingSQL(info.getSchema().getSqlDialect(), vialListSql, col, info.getTempTableName(), null); + } + } + if (!seenVisitValue) + { + vialListSql + .append(prefix) + .append("0.0 AS ") + .append(VISIT_VALUE.getDbColumnName()); + } + + // DrawDate and DrawTime are a little different; + // we need to do the conflict count on DrawTimeStamp and then cast to Date or Time + vialListSql.append(prefix); + appendConflictResolvingSQL(info.getSchema().getSqlDialect(), vialListSql, DRAW_TIMESTAMP, info.getTempTableName(), DRAW_DATE); + vialListSql.append(prefix); + appendConflictResolvingSQL(info.getSchema().getSqlDialect(), vialListSql, DRAW_TIMESTAMP, info.getTempTableName(), DRAW_TIME); + + vialListSql + .append("\nFROM ") + .append(info.getTempTableName()) + .append("\nGROUP BY\n"); + if (!forSpecimenTable) + vialListSql.append(info.getTempTableName()).append(".LSID,\n "); + vialListSql.append(info.getTempTableName()).append(".SpecimenHash"); + if (!forSpecimenTable) + vialListSql.append(",\n ").append(info.getTempTableName()).append(".GlobalUniqueId"); + + return vialListSql; + } + + private void populateVials(SpecimenLoadInfo info, boolean merge, boolean seenVisitValue) throws ValidationException + { + TableInfo specimenTable = getTableInfoSpecimen(); + String specimenTableSelectName = specimenTable.getSelectName(); + TableInfo vialTable = getTableInfoVial(); + String vialTableSelectName = vialTable.getSelectName(); + + String prefix = ",\n "; + SQLFragment insertSelectSql = new SQLFragment() + .append("SELECT exp.Material.RowId") + .append(prefix) + .append(specimenTable.getColumn("RowId").getValueSql(specimenTableSelectName)) + .append(" AS SpecimenId") + .append(prefix) + .append(specimenTable.getColumn("SpecimenHash").getValueSql(specimenTableSelectName)) + .append(prefix) + // Set a default value of true for the 'Available' column: + .append("? AS Available") + .add(Boolean.TRUE); + + for (SpecimenColumn col : getVialCols(info.getAvailableColumns())) + insertSelectSql.append(prefix).append("VialList.").append(col.getLegalDbColumnName(getSqlDialect())); + + insertSelectSql + .append(" FROM (").append(getVialListFromTempTableSql(info, false, seenVisitValue)) + .append(") VialList") + // join to material: + .append("\n JOIN exp.Material ON (") + .append("VialList.LSID = exp.Material.LSID") + .append(" AND exp.Material.Container = ?)") + .add(info.getContainer().getId()) + // join to specimen: + .append("\n JOIN ") + .append(specimenTableSelectName) + .append(" ON ") + .append(specimenTable.getColumn("SpecimenHash").getValueSql(specimenTableSelectName)) + .append(" = VialList.SpecimenHash"); + + if (merge) + { + // Create list of vial columns, including unique columns not found in SPECIMEN_COLUMNS. + Set cols = new LinkedHashSet<>(); + // NOTE: study.Vial.RowId is actually an FK to exp.Material.RowId + cols.add(GLOBAL_UNIQUE_ID); + cols.add(new SpecimenColumn("RowId", "RowId", "INT NOT NULL", TargetTable.VIALS, false)); + cols.add(new SpecimenColumn("SpecimenId", "SpecimenId", "INT NOT NULL", TargetTable.VIALS, false)); + cols.add(new SpecimenColumn("SpecimenHash", "SpecimenHash", "VARCHAR(256)", TargetTable.VIALS, false)); + cols.add(new SpecimenColumn("Available", "Available", ImportTypes.BOOLEAN_TYPE, TargetTable.VIALS, false)); + cols.addAll(getVialCols(info.getAvailableColumns())); + + // Insert or update the vials from in the temp table. + try (TableResultSet rs = new SqlSelector(info.getSchema(), insertSelectSql).getResultSet()) + { + if (VERBOSE_DEBUG) + ResultSetUtil.logData(rs, _logger); + DataIteratorBuilder dib = new DataIteratorBuilder.Wrapper(new ResultsImpl(rs)); + mergeTable(info.getSchema(), vialTableSelectName, vialTable, cols, dib, false, false); + } + catch (SQLException e) + { + throw new RuntimeSQLException(e); + } + } + else + { + // Insert all vials from in the temp table. + SQLFragment insertSql = new SQLFragment() + .append("INSERT INTO ") + .append(vialTableSelectName) + .append("\n(RowId, SpecimenId, SpecimenHash, Available, ") + .append(getVialColsSql(info.getAvailableColumns())) + .append(")\n") + .append(insertSelectSql); + + if (DEBUG) + logSQLFragment(insertSql); + + info("Vials: Inserting new rows..."); + executeSQL(info.getSchema(), insertSql); + info("Vials: Insert complete."); + } + } + + private void logSQLFragment(SQLFragment sql) + { + info(sql.getSQL()); + info("Params: "); + for (Object param : sql.getParams()) + info(param.toString()); + } + + private void populateSpecimenEvents(SpecimenLoadInfo info, boolean merge) throws ValidationException + { + TableInfo specimenEventTable = getTableInfoSpecimenEvent(); + String specimenEventTableSelectName = specimenEventTable.getSelectName(); + TableInfo vialTable = getTableInfoVial(); + String vialTableSelectName = vialTable.getSelectName(); + + SQLFragment insertSelectSql = new SQLFragment() + .append("SELECT ") + .append(vialTable.getColumn("RowId").getValueSql(vialTableSelectName)) + .append(" AS VialId, \n") + .append(getSpecimenEventTempTableColumns(info)) + .append(" FROM ") + .append(info.getTempTableName()) + .append("\nJOIN ") + .append(vialTableSelectName) + .append(" ON ") + .append(info.getTempTableName()) + .append(".GlobalUniqueId = ") + .append(vialTable.getColumn("GlobalUniqueId").getValueSql(vialTableSelectName)); + + if (merge) + { + // Create list of vial columns, including unique columns not found in SPECIMEN_COLUMNS. + // Events are special in that we want to merge based on a pseudo-unique set of columns: + // Container, VialId (vial.GlobalUniqueId), LabId, StorageDate, ShipDate, LabReceiptDate + // We need to always add these extra columns, even if they aren't in the list of available columns. + Set cols = new LinkedHashSet<>(); + cols.add(new SpecimenColumn("VialId", "VialId", "INT NOT NULL", TargetTable.SPECIMEN_EVENTS, true)); + cols.add(LAB_ID); + cols.add(SHIP_DATE); + cols.add(STORAGE_DATE); + cols.add(LAB_RECEIPT_DATE); + + cols.addAll(getSpecimenEventCols(info.getAvailableColumns())); + + // Insert or update the vials from in the temp table. + try (TableResultSet rs = new SqlSelector(info.getSchema(), insertSelectSql).getResultSet()) + { + if (VERBOSE_DEBUG) + ResultSetUtil.logData(rs, _logger); + DataIteratorBuilder dib = new DataIteratorBuilder.Wrapper(new ResultsImpl(rs)); + mergeTable(info.getSchema(), specimenEventTableSelectName, specimenEventTable, cols, dib, false, false); + } + catch (SQLException e) + { + throw new RuntimeSQLException(e); + } + } + else + { + // Insert all events from the temp table + SQLFragment insertSql = new SQLFragment() + .append("INSERT INTO ") + .append(specimenEventTableSelectName) + .append("\n") + .append("(VialId, ") + .append(getSpecimenEventColsSql(info.getAvailableColumns())) + .append(")\n") + .append(insertSelectSql); + + if (DEBUG) + logSQLFragment(insertSql); + + info("Specimen Events: Inserting new rows."); + executeSQL(info.getSchema(), insertSql); + info("Specimen Events: Insert complete."); + } + } + + private interface ComputedColumn + { + String getName(); + Object getValue(Map row) throws ValidationException; + } + + private static class EntityIdComputedColumn implements ComputedColumn + { + @Override + public String getName() { return "EntityId"; } + @Override + public Object getValue(Map row) { return GUID.makeGUID(); } + } + + private Pair, Integer> mergeTable( + DbSchema schema, String tableName, @Nullable TableInfo target, + Collection potentialColumns, DataIteratorBuilder values, boolean addEntityId, boolean hasContainerColumn) + throws ValidationException + { + ComputedColumn entityIdCol = null; + if (addEntityId) + { + entityIdCol = new EntityIdComputedColumn(); + } + + return mergeTable(schema, tableName, target, potentialColumns, values, entityIdCol, hasContainerColumn); + } + + private void mergeTable(DbSchema schema, SpecimenImportFile file, TableInfo target, boolean addEntityId, boolean hasContainerColumn) + throws ValidationException, IOException + { + SpecimenTableType type = file.getTableType(); + + ComputedColumn entityIdCol = null; + + if (addEntityId) + { + entityIdCol = new EntityIdComputedColumn(); + } + + try (DataLoader loader = loadTsv(file)) + { + mergeTable(schema, type.getTableName(), target, type.getColumns(), loader, entityIdCol, hasContainerColumn); + } + finally + { + file.getStrategy().close(); + } + } + + /** + * Insert or update rows on the target table using the unique columns of potentialColumns + * to identify the existing row. + * + * NOTE: The idCol is used only during insert -- the value won't be updated if the row already exists. + * + * @param schema The dbschema. + * @param idCol The computed column. + * @return A pair of the columns actually found in the data values and a total row count. + * @throws org.labkey.api.query.ValidationException + */ + private Pair, Integer> mergeTable( + DbSchema schema, String tableName, @Nullable TableInfo target, + Collection potentialColumns, DataIteratorBuilder values, + ComputedColumn idCol, boolean hasContainerColumn) + throws ValidationException + { + // tests SpecimenTest, LuminexUploadAndCopyTest, VaccineProtocolTest, FlowSpecimenTest, SpecimenImportTest, CreateVialsTest, ViabilityTest + if (values == null) + { + info(tableName + ": No rows to merge"); + return new Pair<>(Collections.emptyList(), 0); + } + + if (null == target) + { + target = schema.getTable(tableName.substring(tableName.indexOf('.') + 1)); + if (null == target) + throw new IllegalArgumentException("tablename: " + tableName); + } + + // get the iter 'early' so we can look at the columns + + DataIteratorContext dix = new DataIteratorContext(); + dix.setInsertOption(QueryUpdateService.InsertOption.MERGE); + DataIterator iter = values.getDataIterator(dix); + + CaseInsensitiveHashSet tsvColumnNames = new CaseInsensitiveHashSet(); + for (int i=1 ; i<= iter.getColumnCount() ; i++) + tsvColumnNames.add(iter.getColumnInfo(i).getName()); + + List availableColumns = new ArrayList<>(); + CaseInsensitiveHashSet skipColumns = new CaseInsensitiveHashSet(target.getColumnNameSet()); + CaseInsensitiveHashSet keyColumns = new CaseInsensitiveHashSet(); + + CaseInsensitiveHashSet dontUpdate = new CaseInsensitiveHashSet(); + if (null != idCol) + dontUpdate.add(idCol.getName()); + dontUpdate.add("entityid"); + skipColumns.remove("entityid"); + + // NOTE entityid is handled by DataIterator so ignore EntityIdComputedColumn + if (idCol instanceof EntityIdComputedColumn) + idCol = null; + + for (T column : potentialColumns) + { + if (tsvColumnNames.contains(column.getPrimaryTsvColumnName()) || tsvColumnNames.contains(column.getDbColumnName())) + { + availableColumns.add(column); + skipColumns.remove(column.getDbColumnName()); + } + } + + for (T col : availableColumns) + { + if (col.isUnique()) + keyColumns.add(col.getDbColumnName()); + } + if (hasContainerColumn) + { + keyColumns.add("Container"); + skipColumns.remove("Container"); + } + if (idCol != null) + skipColumns.remove(idCol.getName()); + if (keyColumns.isEmpty()) + keyColumns = null; + + DataIteratorBuilder specimenIter = new SpecimenImportBuilder(new DataIteratorBuilder.Wrapper(iter), potentialColumns, Collections.singletonList(idCol)); + DataIteratorBuilder std = StandardDataIteratorBuilder.forInsert(target, specimenIter, getContainer(), getUser(), dix); + DataIteratorBuilder tableIter = new TableInsertDataIteratorBuilder(std, target, getContainer()) + .setKeyColumns(keyColumns) + .setAddlSkipColumns(skipColumns) + .setDontUpdate(dontUpdate); + + assert !_specimensTableType.getTableName().equalsIgnoreCase(tableName); + info(tableName + ": Starting merge of data..."); + + Pump pump = new Pump(tableIter, dix); + pump.run(); + if (dix.getErrors().hasErrors()) + throw dix.getErrors().getLastRowError(); + int rowCount = pump.getRowCount(); + + info(tableName + "updated or inserted " + rowCount + " rows of data"); + return new Pair<>(availableColumns, rowCount); + } + + private void replaceTable(DbSchema schema, SpecimenImportFile file, TableInfo target, boolean addEntityId, boolean hasContainerColumn) + throws IOException, ValidationException + { + ComputedColumn entityIdCol = null; + if (addEntityId) + { + entityIdCol = new EntityIdComputedColumn(); + } + + replaceTable(schema, file, file.getTableType().getTableName(), target, false, hasContainerColumn, null, null, entityIdCol); + } + + /** + * Deletes the target table and inserts new rows. + * + * @param schema The dbschema + * @param file SpecimenImportFile + * @param tableName Fully qualified table name, e.g., "study.Vials" + * @param generateGlobaluniqueIds Generate globalUniqueIds if any needed + * @param hasContainerColumn + * @param drawDate DrawDate column or null + * @param drawTime DrawTime column or null + * @return A pair of the columns actually found in the data values and a total row count. + * @throws IOException + */ + public Pair, Integer> replaceTable( + DbSchema schema, SpecimenImportFile file, String tableName, @Nullable TableInfo target, + boolean generateGlobaluniqueIds, boolean hasContainerColumn, ComputedColumn drawDate, ComputedColumn drawTime, + ComputedColumn... computedColumnsAddl) + throws IOException, ValidationException + { + if (file == null) + { + info(tableName + ": No rows to replace"); + return new Pair<>(Collections.emptyList(), 0); + } + + ensureNotCanceled(); + info(tableName + ": Starting replacement of all data..."); + + assert !_specimensTableType.getTableName().equalsIgnoreCase(tableName); + if (hasContainerColumn) + executeSQL(schema, "DELETE FROM " + tableName + " WHERE Container = ?", getContainer().getId()); + else + executeSQL(schema, "DELETE FROM " + tableName); + + ArrayList computedColumns = new ArrayList<>(); + if (null != drawDate) + computedColumns.add(drawDate); + if (null != drawTime) + computedColumns.add(drawTime); + computedColumns.addAll(Arrays.asList(computedColumnsAddl)); + + final List newUniqueIds = (generateGlobaluniqueIds && _generateGlobalUniqueIds > 0) ? + getValidGlobalUniqueIds(_generateGlobalUniqueIds) : null; + + if (null != newUniqueIds) + { + computedColumns.add(new ComputedColumn() + { + int idCount = 0; + + @Override + public String getName() + { + return GLOBAL_UNIQUE_ID_TSV_COL; + } + + @Override + public Object getValue(Map row) + { + Object uniqueid = row.get(GLOBAL_UNIQUE_ID_TSV_COL); + if (null == uniqueid) + uniqueid = newUniqueIds.get(idCount++); + return uniqueid; + } + }); + } + + int rowCount; + ColumnDescriptor[] tsvColumns; + + try + { + if (null == target) + { + String dbname = tableName; + if (dbname.startsWith(schema.getName() + ".")) + dbname = dbname.substring(schema.getName().length() + 1); + target = schema.getTable(dbname); + } + if (null == target) + throw new IllegalStateException("Could not resolve table: " + tableName); + + DataIteratorContext dix = new DataIteratorContext(); + dix.setInsertOption(QueryUpdateService.InsertOption.IMPORT); + DataLoader tsv = loadTsv(file); + tsvColumns = tsv.getColumns(); + +/* // DEBUG: Dump data + StringBuilder infoCol = new StringBuilder(""); + for (ColumnDescriptor cd : tsvColumns) + infoCol.append(cd.getColumnName() + ", "); + info(infoCol.toString()); + String[][] lines = tsv.getFirstNLines(15); + for (String[] line : lines) + { + StringBuilder infoRow = new StringBuilder(""); + for (String item : line) + infoRow.append(item + ", "); + info(infoRow.toString()); + } + info(""); +*/ + // CONSIDER turn off data conversion + //for (ColumnDescriptor cd : tsvColumns) cd.clazz = String.class; + // CONSIDER use AsyncDataIterator + //DataIteratorBuilder asyncIn = new AsyncDataIterator.Builder(tsv); + DataIteratorBuilder asyncIn = tsv; + DataIteratorBuilder specimenWrapped = new SpecimenImportBuilder(asyncIn, file.getTableType().getColumns(), computedColumns); + DataIteratorBuilder standardEtl = StandardDataIteratorBuilder.forInsert(target, specimenWrapped, getContainer(), getUser(), dix); + DataIteratorBuilder persist = ((UpdateableTableInfo)target).persistRows(standardEtl, dix); + Pump pump = new Pump(persist, dix); + pump.setProgress(new ListImportProgress() + { + long heartBeat = HeartBeat.currentTimeMillis(); + + @Override + public void setTotalRows(int rows) + { + } + + @Override + public void setCurrentRow(int currentRow) + { + if (0 == currentRow % SQL_BATCH_SIZE) + { + if (0 == currentRow % (SQL_BATCH_SIZE*100)) + info(currentRow + " rows loaded..."); + long hb = HeartBeat.currentTimeMillis(); + if (hb == heartBeat) + return; + ensureNotCanceled(); + heartBeat = hb; + } + } + }); + pump.run(); + if (dix.getErrors().hasErrors()) + { + throw new ValidationException(dix.getErrors().getLastRowError().getMessage() + " (File: " + file.getTableType().getName() + ")"); + } + rowCount = pump.getRowCount(); + + info(tableName + ": Replaced all data with " + rowCount + " new rows."); + } + finally + { + file.getStrategy().close(); + } + + // Note: this duplicates the logic in SpecimenImportIterator (just below). Keep these code paths in sync. + Map importMap = (Map)createImportMap(file.getTableType().getColumns()); + final var seen = new HashSet(); + List availableColumns = Arrays.stream(tsvColumns).map(tsv -> importMap.get(tsv.getColumnName())) + .filter(Objects::nonNull) + .filter(cd -> seen.add(cd.getPrimaryTsvColumnName())) + .collect(Collectors.toList()); + return new Pair<>(availableColumns, rowCount); + } + + static Map createImportMap(Collection importColumns) + { + var map = new CaseInsensitiveHashMap(); + for (var c : importColumns) + map.put(c.getDbColumnName(), c); + for (var c : importColumns) + c.getImportAliases().forEach(n -> map.put(n, c)); + for (var c : importColumns) + map.put(c.getPrimaryTsvColumnName(), c); + return map; + } + + + private class SpecimenImportBuilder implements DataIteratorBuilder + { + private final DataIteratorBuilder dib; + private final Collection importColumns; + private final List computedColumns; + + SpecimenImportBuilder(DataIteratorBuilder in, Collection importColumns, List computedColumns) + { + dib = in; + this.importColumns = importColumns; + this.computedColumns = computedColumns; + } + + @Override + public DataIterator getDataIterator(final DataIteratorContext context) + { + DataIterator in = dib.getDataIterator(context); + DataIterator aliased = LoggingDataIterator.wrap(createAliasDataIterator(this, in, context)); + return LoggingDataIterator.wrap(new SpecimenImportIterator(this, DataIteratorUtil.wrapMap(aliased, false), context)); + } + } + + // TODO We should consider trying to let the Standard DataIterator "import alias" replace some of this ImportableColumn behavior + // TODO that might let us switch SpecimenImportBuilder to after StandardDataIteratorBuilder instead of before + // TODO Also, this would allow us to _not_ have TsvLoader do type conversion. see loadTsv(). + DataIterator createAliasDataIterator(SpecimenImportBuilder sib, DataIterator in, DataIteratorContext context) + { + var importMap = createImportMap(sib.importColumns); + ArrayList names = new ArrayList<>(in.getColumnCount()+1); + names.add(null); + for (int i=1 ; i<=in.getColumnCount() ; i++) + { + ImportableColumn c = importMap.get(in.getColumnInfo(i).getName()); + names.add(c == null ? null : c.getPrimaryTsvColumnName()); + } + return AliasDataIterator.wrap(in, context, names); + } + + public static class AliasDataIterator extends SimpleTranslator + { + static DataIterator wrap(DataIterator in, DataIteratorContext context, List names) + { + boolean hasAlias = false; + for (int i=1 ; i<=in.getColumnCount() ; i++) + { + String name = i < names.size() ? names.get(i) : null; + hasAlias |= name != null && !name.equals(in.getColumnInfo(i).getName()); + } + if (!hasAlias) + return in; + return new AliasDataIterator(in, context, names); + } + + /** names should be one based so they match the column indexes */ + AliasDataIterator(DataIterator in, DataIteratorContext context, List names) + { + super(in, context); + + for (int i=1 ; i<=in.getColumnCount() ; i++) + { + String name = i < names.size() ? names.get(i) : null; + if (null != name) + addColumn(name, i); + else + addColumn(i); + } + } + } + + + // TODO StandardDataIteratorBuilder should be enforcing max length + private class SpecimenImportIterator extends SimpleTranslator + { + private Map _rowMap; + + SpecimenImportIterator(SpecimenImportBuilder sib, MapDataIterator in, DataIteratorContext context) + { + super(in, context); + + SqlDialect d = DbSchema.getTemp().getSqlDialect(); + + CaseInsensitiveHashSet tsvColumnNames = new CaseInsensitiveHashSet(); + for (int i=1 ; i<= in.getColumnCount() ; i++) + tsvColumnNames.add(in.getColumnInfo(i).getName()); + + // deal with computedColumns that might mask importColumns + CaseInsensitiveHashSet seen = new CaseInsensitiveHashSet(); + + for (final ComputedColumn cc : sib.computedColumns) + { + if (null != cc && seen.add(cc.getName())) + { + ColumnInfo col = new BaseColumnInfo(cc.getName(), JdbcType.OTHER); + Callable call = () -> { + Object computedValue = cc.getValue(_rowMap); + if (computedValue instanceof Parameter.TypedValue) + return ((Parameter.TypedValue) computedValue).getJdbcParameterValue(); + else + return computedValue; + }; + addColumn(col, call); + } + } + + for (final ImportableColumn ic : sib.importColumns) + { + if (seen.add(ic.getLegalDbColumnName(d))) + { + String boundInputColumnName = null; + if (tsvColumnNames.contains(ic.getPrimaryTsvColumnName())) + boundInputColumnName = ic.getPrimaryTsvColumnName(); + final String name = boundInputColumnName; + ColumnInfo col = new BaseColumnInfo(ic.getLegalDbColumnName(d), ic.getJdbcType()); + Supplier call = () -> { + Object ret = null; + if (null != name) + ret = _rowMap.get(name); + if (null == ret) + ret = ic.getDefaultValue(); + + if (ic.getMaxSize() >= 0 && ret instanceof String) + { + if (((String)ret).length() > ic.getMaxSize()) + { + @SuppressWarnings("ThrowableNotThrown") + var rowError = getRowError(); + rowError.addFieldError(ic.getPrimaryTsvColumnName(), "Value \"" + ret + "\" is too long for column " + + ic.getDbColumnName() + ". The maximum allowable length is " + ic.getMaxSize() + "."); + } + } + + return ret; + }; + addColumn(col, call); + } + } + } + + @Override + protected void processNextInput() + { + _rowMap = ((MapDataIterator)getInput()).getMap(); + } + } + + private DataLoader loadTsv(@NotNull SpecimenImportFile importFile) throws IOException + { + assert null != importFile; + + SpecimenTableType type = importFile.getTableType(); + String tableName = type.getTableName(); + + info(tableName + ": Parsing data file for table..."); + + var expectedColumns = createImportMap(type.getColumns()); + + DataLoader loader = importFile.getDataLoader(); + + for (ColumnDescriptor column : loader.getColumns()) + { + var expectedColumn = expectedColumns.get(column.name.toLowerCase()); + + if (expectedColumn != null) + { + column.clazz = expectedColumn.getJavaClass(); + if (VISIT_COL.equals(column.name)) + column.clazz = String.class; + } + else + { + column.load = false; + } + } + + return loader; + } + + private Pair, Integer> populateTempTable(TempTablesHolder tempTablesHolder, SpecimenImportFile file, boolean merge) + throws IOException, ValidationException + { + info("Populating specimen temp table..."); + TempTableInfo tempTableInfo = tempTablesHolder.getTempTableInfo(); + int rowCount; + List loadedColumns = new ArrayList<>(); + + ComputedColumn lsidCol = new ComputedColumn() + { + @Override + public String getName() { return "LSID"; } + @Override + public Object getValue(Map row) throws ValidationException + { + String id = (String) row.get(GLOBAL_UNIQUE_ID_TSV_COL); + if (id == null) + id = (String) row.get(SPEC_NUMBER_TSV_COL); + + if (id == null) + { + throw new ValidationException("GlobalUniqueId is required but was not supplied"); + } + + Lsid lsid = SpecimenService.get().getSpecimenMaterialLsid(getContainer(), id); + return lsid.toString(); + } + }; + + // remove VISIT_COL since that's a computed column + // 1) should that be removed from SPECIMEN_COLUMNS? + // 2) convert this to DataIterator? + SpecimenColumn _visitCol = null; + SpecimenColumn _participantIdCol = null; + for (SpecimenColumn sc : _specimenColumns) + { + if (StringUtils.equals("VisitValue", sc.getDbColumnName())) + _visitCol = sc; + else if (StringUtils.equals("Ptid", sc.getDbColumnName())) + _participantIdCol = sc; + } + + final Study study = StudyService.get().getStudy(getContainer()); + final ImportHelperService ihs = ImportHelperService.get(); + final SequenceNumTranslator snt = ihs.getSequenceNumTranslator(study); + final ParticipantIdTranslator pit = ihs.getParticipantIdTranslator(study, getUser()); + final SpecimenColumn visitCol = _visitCol; + final SpecimenColumn dateCol = DRAW_TIMESTAMP; + final SpecimenColumn participantIdCol = _participantIdCol; + final Parameter.TypedValue nullDouble = Parameter.nullParameter(JdbcType.DOUBLE); + + ComputedColumn computedParticipantIdCol = new ComputedColumn() + { + @Override + public String getName() + { + return participantIdCol.getDbColumnName(); + } + + @Override + public Object getValue(Map row) throws ValidationException + { + Object p = SpecimenImporter.this.getValue(participantIdCol, row); + return pit.translateParticipantId(p); + } + }; + + ComputedColumn sequencenumCol = new ComputedColumn() + { + @Override + public String getName() + { + return visitCol.getDbColumnName(); + } + + @Override + public Object getValue(Map row) throws ValidationException + { + Object s = SpecimenImporter.this.getValue(visitCol, row); + Object d = SpecimenImporter.this.getValue(dateCol, row); + Double sequencenum = snt.translateSequenceNum(s, d); +// if (sequencenum == null) +// throw new org.apache.commons.beanutils.ConversionException("No visit_value provided: visit_value=" + String.valueOf(s) + " draw_timestamp=" + String.valueOf(d)); + if (null == sequencenum) + return nullDouble; + return sequencenum; + } + }; + + ComputedColumn drawDateCol = new ComputedColumn() + { + @Override + public String getName() + { + return "DrawDate"; + } + + @Override + public Object getValue(Map row) + { + Object d = SpecimenImporter.this.getValue(dateCol, row); + return DateUtil.getDateOnly((Date) d); + } + }; + + ComputedColumn drawTimeCol = new ComputedColumn() + { + @Override + public String getName() + { + return "DrawTime"; + } + + @Override + public Object getValue(Map row) + { + Object d = SpecimenImporter.this.getValue(dateCol, row); + return DateUtil.getTimeOnly((Date)d); + } + }; + + Pair, Integer> pair = new Pair<>(null, 0); + boolean success = true; + final int MAX_TRYS = 3; + for (int tryCount = 0; tryCount < MAX_TRYS; tryCount += 1) + { + try + { + pair = replaceTable(tempTableInfo.getSchema(), file, tempTableInfo.getSelectName(), tempTableInfo, true, false, drawDateCol, drawTimeCol, + lsidCol, sequencenumCol, computedParticipantIdCol); + + loadedColumns = pair.first; + rowCount = pair.second; + + if (rowCount == 0) + { + info("Found no specimen columns to import. Temp table will not be loaded."); + return pair; + } + + remapTempTableLookupIndexes(tempTableInfo.getSchema(), tempTableInfo.getSelectName(), loadedColumns); + + updateTempTableVisits(tempTableInfo.getSchema(), tempTableInfo.getSelectName()); + + if (merge) + { + checkForConflictingSpecimens(tempTableInfo.getSchema(), tempTableInfo.getSelectName(), loadedColumns); + } + } + catch (OptimisticConflictException e) + { + if (tryCount + 1 < MAX_TRYS) + success = false; // Try again + else + throw e; + } + if (success) + break; + } + + updateTempTableSpecimenHash(tempTablesHolder, loadedColumns); + + info("Specimen temp table populated."); + return pair; + } + + protected void remapTempTableLookupIndexes(DbSchema schema, String tempTable, List loadedColumns) + { + String sep = ""; + SQLFragment remapExternalIdsSql = new SQLFragment("UPDATE ").append(tempTable).append(" SET "); + for (SpecimenColumn col : loadedColumns) + { + if (col.getFkTable() != null) + { + remapExternalIdsSql.append(sep).append(col.getLegalDbColumnName(getSqlDialect())).append(" = (SELECT RowId FROM ") + .append(getTableInfoFromFkTableName(col.getFkTable()).getSelectName()).append(" ").append(col.getFkTableAlias()) + .append(" WHERE ").append("(").append(tempTable).append(".") + .append(col.getLegalDbColumnName(getSqlDialect())).append(" = ").append(col.getFkTableAlias()).append(".").append(col.getFkColumn()) + .append("))"); + sep = ",\n\t"; + } + } + + info("Remapping lookup indexes in temp table..."); + if (DEBUG) + info(remapExternalIdsSql.toDebugString()); + if (!sep.isBlank()) + executeSQL(schema, remapExternalIdsSql); + info("Update complete."); + } + + private void updateTempTableVisits(DbSchema schema, String tempTable) + { + Study study = StudyService.get().getStudy(getContainer()); + if (study.getTimepointType() != TimepointType.VISIT) + { + info("Updating visit values to match draw timestamps (date-based studies only)..."); + SQLFragment visitValueSql = new SQLFragment() + .append("UPDATE ") + .append(tempTable) + .append(" SET VisitValue = (") + .append(StudyUtils.sequenceNumFromDateSQL("DrawTimestamp")) + .append(")"); + if (DEBUG) + info(visitValueSql.toDebugString()); + executeSQL(schema, visitValueSql); + info("Update complete."); + } + } + + protected void checkForConflictingSpecimens(DbSchema schema, String tempTable, List loadedColumns) + { + if (!SettingsManager.get().getRepositorySettings(getContainer()).isSpecimenDataEditable()) + { + info("Checking for conflicting specimens before merging..."); + + // Columns used in the specimen hash + StringBuilder hashCols = new StringBuilder(); + for (SpecimenColumn col : loadedColumns) + { + if (col.getTargetTable().isSpecimens() && col.getAggregateEventFunction() == null) + { + hashCols.append(",\n\t"); + hashCols.append(col.getLegalDbColumnName(getSqlDialect())); + } + } + hashCols.append("\n"); + + SQLFragment existingEvents = new SQLFragment("SELECT GlobalUniqueId") + .append(hashCols) + .append("FROM ") + .append(getTableInfoVial(), "Vial") + .append("\n") + .append("JOIN ").append(getTableInfoSpecimen(), "Specimen") + .append("\n") + .append("ON Vial.SpecimenId = Specimen.RowId\n") + .append("WHERE Vial.GlobalUniqueId IN (SELECT GlobalUniqueId FROM ") + .append(tempTable) + .append(")\n"); + + SQLFragment tempTableEvents = new SQLFragment("SELECT GlobalUniqueId") + .append(hashCols) + .append("FROM ") + .append(tempTable); + + // "UNION ALL" the temp and the existing tables and group by columns used in the specimen hash + SQLFragment allEventsByHashCols = new SQLFragment("SELECT COUNT(*) AS Group_Count, * FROM (\n") + .append("(\n").append(existingEvents).append("\n)\n") + .append("UNION ALL /* SpecimenImporter.checkForConflictingSpecimens() */\n") + .append("(\n").append(tempTableEvents).append("\n)\n") + .append(") U\n") + .append("GROUP BY GlobalUniqueId") + .append(hashCols); + + Map>> rowsByGUID = new HashMap<>(); + Set duplicateGUIDs = new TreeSet<>(); + + Map[] allEventsByHashColsResults = new SqlSelector(schema, allEventsByHashCols).getMapArray(); + + for (Map row : allEventsByHashColsResults) + { + String guid = (String)row.get("GlobalUniqueId"); + if (guid != null) + { + if (rowsByGUID.containsKey(guid)) + { + // Found a duplicate + List> dups = rowsByGUID.get(guid); + dups.add(row); + duplicateGUIDs.add(guid); + } + else + { + rowsByGUID.put(guid, new ArrayList<>(Arrays.asList(row))); + } + } + } + + if (duplicateGUIDs.size() == 0) + { + info("No conflicting specimens found"); + } + else + { + StringBuilder sb = new StringBuilder(); + for (String guid : duplicateGUIDs) + { + List> dups = rowsByGUID.get(guid); + if (dups != null && dups.size() > 0) + { + if (sb.length() > 0) + sb.append("\n"); + sb.append("Conflicting specimens found for GlobalUniqueId '").append(guid).append("':\n"); + + for (Map row : dups) + { + // CONSIDER: if we want to be really fancy, we could diff the columns to find the conflicting value. + for (SpecimenColumn col : loadedColumns) + if (col.getTargetTable().isSpecimens() && col.getAggregateEventFunction() == null) + sb.append(" ").append(col.getDbColumnName()).append("=").append(row.get(col.getDbColumnName())).append("\n"); + sb.append("\n"); + } + } + } + + _logger.error(sb.toString()); + + // If conflicts are found, stop the import. + throw new IllegalStateException(sb.toString()); + } + } + else + { + // Check if any incoming vial is already present in the vial table; this is not allowed + info("Checking for conflicting specimens in editable repsoitory..."); + SQLFragment sql = new SQLFragment("SELECT COUNT(*) FROM ").append(getTableInfoVial().getSelectName()); + sql.append(" WHERE GlobalUniqueId IN " + "(SELECT GlobalUniqueId FROM "); + sql.append(tempTable).append(")"); + ArrayList counts = new SqlSelector(schema, sql).getArrayList(Integer.class); + if (1 != counts.size()) + { + throw new IllegalStateException("Expected one and only one count of rows."); + } + else if (0 != counts.get(0) && _generateGlobalUniqueIds > 0) + { + // We were trying to generate globalUniqueIds + throw new OptimisticConflictException("Attempt to generate global unique ids failed.", null, 0); + } + else if (0 != counts.get(0)) + { + throw new IllegalStateException("With an editable specimen repository, importing may not reference any existing specimen. " + + counts.get(0) + " imported specimen events refer to existing specimens.") ; + } + info("No conflicting specimens found"); + } + } + + private void updateTempTableSpecimenHash(TempTablesHolder tempTablesHolder, List loadedColumns) + { + DbSchema schema = tempTablesHolder.getTempTableInfo().getSchema(); + String tempTableName = tempTablesHolder.getTempTableInfo().getSelectName(); + String selectInsertTempTableName = tempTablesHolder.getSelectInsertTempTableInfo().getSelectName(); + + // NOTE: In merge case, we've already checked the specimen hash columns are not in conflict. + SQLFragment conflictResolvingSubselect = new SQLFragment("SELECT GlobalUniqueId"); + for (SpecimenColumn col : loadedColumns) + { + if (col.getTargetTable().isSpecimens()) + { + conflictResolvingSubselect.append(",\n\t"); + String selectCol = tempTableName + "." + col.getLegalDbColumnName(getSqlDialect()); + + if (col.getAggregateEventFunction() != null) + conflictResolvingSubselect.append(col.getAggregateEventFunction()).append("(").append(selectCol).append(")"); + else + { + String singletonAggregate; + if (col.getJavaClass().equals(Boolean.class)) + { + // gross nested calls to cast the boolean to an int, get its min, then cast back to a boolean. + // this is needed because most aggregates don't work on boolean values. + singletonAggregate = "CAST(MIN(CAST(" + selectCol + " AS INTEGER)) AS " + schema.getSqlDialect().getBooleanDataType() + ")"; + } + else + { + singletonAggregate = "MIN(" + selectCol + ")"; + } + conflictResolvingSubselect.append("CASE WHEN"); + conflictResolvingSubselect.append(" COUNT(DISTINCT ").append(selectCol).append(") = 1 THEN "); + conflictResolvingSubselect.append(singletonAggregate); + conflictResolvingSubselect.append(" ELSE NULL END"); + } + conflictResolvingSubselect.append(" AS ").append(col.getLegalDbColumnName(getSqlDialect())); + } + } + conflictResolvingSubselect.append("\nFROM ").append(tempTableName).append("\nGROUP BY GlobalUniqueId"); + + SQLFragment updateHashSql = new SQLFragment("SELECT ("); + makeUpdateSpecimenHashSql(schema, getContainer(), loadedColumns, "InnerTable.", updateHashSql); + updateHashSql.append(") AS SpecimenHash, ") + .append("InnerTable.GlobalUniqueId") + .append("\n\tINTO ") + .append(selectInsertTempTableName) + .append("\n\tFROM (") + .append(conflictResolvingSubselect) + .append(") InnerTable"); + + info("Calculating specimen hash values into second temp table..."); + if (DEBUG) + info(updateHashSql.toDebugString()); + executeSQL(schema, updateHashSql); + info("Done calculating specimen hash values."); + tempTablesHolder.getSelectInsertTempTableInfo().track(); // We've now created the second temp table + + SQLFragment setSpecimenHashSql = new SQLFragment("UPDATE ") + .append(tempTableName) + .append(" SET SpecimenHash = InnerTable.SpecimenHash\nFROM ") + .append(selectInsertTempTableName) + .append(" InnerTable\nWHERE ") + .append(tempTableName) + .append(".GlobalUniqueId = InnerTable.GlobalUniqueId"); + + info("Updating specimen hash values in temp table..."); + if (DEBUG) + info(setSpecimenHashSql.toDebugString()); + executeSQL(schema, setSpecimenHashSql); + info("Update complete."); + info("Temp table populated."); + } + + public static void makeUpdateSpecimenHashSql(DbSchema schema, Container container, List loadedColumns, String innerTable, SQLFragment updateHashSql) + { + ArrayList hash = new ArrayList<>(); + hash.add(new SQLFragment("?").add("Fld-" + container.getRowId())); + + Map loadedColumnMap = new HashMap<>(); + loadedColumns.forEach(col -> loadedColumnMap.put(col.getPrimaryTsvColumnName(), col)); + final String strType = schema.getSqlDialect().getSqlCastTypeName(JdbcType.VARCHAR); + SpecimenColumns.BASE_SPECIMEN_COLUMNS.forEach(col -> { + if (col.getTargetTable().isSpecimens()) + { + if (loadedColumnMap.isEmpty() || loadedColumnMap.containsKey(col.getPrimaryTsvColumnName())) + { + String columnName = innerTable + col.getLegalDbColumnName(schema.getSqlDialect()); + hash.add(new SQLFragment("'~'")); + hash.add(new SQLFragment(" CASE WHEN " + columnName + " IS NOT NULL THEN CAST(" + columnName + " AS " + strType + ") ELSE '' END")); + } + else + { + hash.add(new SQLFragment("'~'")); + } + } + }); + + updateHashSql.append(schema.getSqlDialect().concatenate(hash.toArray(new SQLFragment[0]))); + } + + private Object getValue(ImportableColumn col, Map tsvRow) + { + Object value = null; + if (tsvRow.containsKey(col.getPrimaryTsvColumnName())) + value = tsvRow.get(col.getPrimaryTsvColumnName()); + else if (tsvRow.containsKey(col.getDbColumnName())) + value = tsvRow.get(col.getDbColumnName()); + return value; + } + + private static final boolean DEBUG = false; + private static final boolean VERBOSE_DEBUG = false; + + private static class TempTablesHolder + { + private final TempTableInfo _tempTableInfo; // main specimen temp table + private final TempTableInfo _selectInsertTempTableInfo; // temp table used to Select Insert into while populating main temp table + private final Runnable _createIndexes; + + public TempTablesHolder(TempTableInfo tempTableInfo, TempTableInfo selectInsertTempTableInfo, Runnable createIndexes) + { + _tempTableInfo = tempTableInfo; + _selectInsertTempTableInfo = selectInsertTempTableInfo; + _createIndexes = createIndexes; + } + + public TempTableInfo getTempTableInfo() { + return _tempTableInfo; + } + + public TempTableInfo getSelectInsertTempTableInfo() { + return _selectInsertTempTableInfo; + } + + public Runnable getCreateIndexes() { + return _createIndexes; + } + } + + private TempTablesHolder createTempTable() + { + info("Creating temp table to hold archive data..."); + SqlDialect dialect = DbSchema.getTemp().getSqlDialect(); + + StringBuilder sql = new StringBuilder(); + String uniquifier = StringUtilsLabKey.getUniquifier(9); + + ArrayList columns = new ArrayList<>(); + + String strType = dialect.getSqlTypeName(JdbcType.VARCHAR); + + sql.append("\n(\n RowId ").append(dialect.getUniqueIdentType()).append(", "); + columns.add(new BaseColumnInfo("RowId", JdbcType.INTEGER, 0, false)); + columns.get(0).setAutoIncrement(true); + + sql.append("LSID ").append(strType).append("(300) NOT NULL, "); + columns.add(new BaseColumnInfo("LSID", JdbcType.VARCHAR, 300, false)); + + sql.append("SpecimenHash ").append(strType).append("(300), "); + columns.add(new BaseColumnInfo("SpecimenHash", JdbcType.VARCHAR, 300, true)); + + sql.append(DRAW_DATE.getDbColumnName()).append(" ").append(DRAW_DATE.getDbType()).append(", "); + columns.add(new BaseColumnInfo(DRAW_DATE.getDbColumnName(), DRAW_DATE.getJdbcType(), 0, true)); + + sql.append(DRAW_TIME.getDbColumnName()).append(" ").append(DRAW_TIME.getDbType()); + columns.add(new BaseColumnInfo(DRAW_TIME.getDbColumnName(), DRAW_TIME.getJdbcType(), 0, true)); + + for (SpecimenColumn col : _specimenColumns) + { + String name = col.getLegalDbColumnName(getSqlDialect()); + sql.append(",\n ").append(name).append(" ").append(col.getDbType()); + BaseColumnInfo colInfo = new BaseColumnInfo(name, col.getJdbcType(), col.getMaxSize(), true); + Collection aliases = col.getImportAliases(); + if (!aliases.isEmpty()) + colInfo.setImportAliasesSet(new HashSet<>(aliases)); + columns.add(colInfo); + } + sql.append("\n)"); + + TempTableInfo tempTableInfo = new TempTableInfo("SpecimenUpload", (List)(List)columns, Arrays.asList("RowId")); + final String fullTableName = tempTableInfo.getSelectName(); + + sql.insert(0, "CREATE TABLE " + fullTableName + " "); + executeSQL(DbSchema.getTemp(), sql); + tempTableInfo.track(); + + // globalUniquId + final String globalUniqueIdIndexSql = "CREATE INDEX IX_SpecimenUpload" + uniquifier + "_GlobalUniqueId ON " + fullTableName + "(GlobalUniqueId)"; + if (DEBUG) + info(globalUniqueIdIndexSql); + executeSQL(DbSchema.getTemp(), globalUniqueIdIndexSql); + + // We'll Insert Into this one with the calculated specimenHashes and then Update the temp table from there + ArrayList columns2 = new ArrayList<>(); + columns2.add(new BaseColumnInfo(GLOBAL_UNIQUE_ID.getDbColumnName(), GLOBAL_UNIQUE_ID.getJdbcType(), GLOBAL_UNIQUE_ID.getMaxSize(), true)); + columns2.add(new BaseColumnInfo("SpecimenHash", JdbcType.VARCHAR, 300, true)); + TempTableInfo selectInsertTempTableInfo = new TempTableInfo("SpecimenUpload2", columns2, Collections.singletonList("RowId")); + + final String rowIdIndexSql = "CREATE INDEX IX_SpecimenUpload" + uniquifier + "_RowId ON " + fullTableName + "(RowId)"; + final String lsidIndexSql = "CREATE INDEX IX_SpecimenUpload" + uniquifier + "_LSID ON " + fullTableName + "(LSID)"; + final String hashIndexSql = "CREATE INDEX IX_SpecimenUpload" + uniquifier + "_SpecimenHash ON " + fullTableName + "(SpecimenHash)"; + + // delay remaining indexes + Runnable createIndexes = () -> { + if (DEBUG) + { + info(rowIdIndexSql); + info(lsidIndexSql); + info(hashIndexSql); + } + executeSQL(DbSchema.getTemp(), rowIdIndexSql); + executeSQL(DbSchema.getTemp(), lsidIndexSql); + executeSQL(DbSchema.getTemp(), hashIndexSql); + info("Created indexes on table " + fullTableName); + }; + + info("Created temporary table " + fullTableName); + return new TempTablesHolder(tempTableInfo, selectInsertTempTableInfo, createIndexes); + } + + + private static final String SPECIMEN_SEQUENCE_NAME = "org.labkey.study.samples"; + + private List getValidGlobalUniqueIds(int count) + { + List uniqueIds = new ArrayList<>(); + DbSequence sequence = DbSequenceManager.get(getContainer(), SPECIMEN_SEQUENCE_NAME); + sequence.ensureMinimum(70000); + + Sort sort = new Sort(); + sort.appendSortColumn(FieldKey.fromString("GlobalUniqueId"), Sort.SortDirection.DESC, false); + Set columns = Collections.singleton("GlobalUniqueId"); + Set currentIds = new HashSet<>(new TableSelector(getTableInfoVial(), columns, null, sort).getArrayList(String.class)); + + for (int i = 0; i < count; i += 1) + { + while (true) + { + String id = String.valueOf(sequence.next()); + + if (!currentIds.contains(id)) + { + uniqueIds.add(id); + break; + } + } + } + + return uniqueIds; + } + + + @TestWhen(TestWhen.When.BVT) + public static class TestCase extends Assert + { + private static final String TABLE = "SpecimenImporterTest"; + + private TempTableInfo _simpleTable; + + @Before + public void createTable() + { + List columns = new ArrayList<>(); + columns.add(new BaseColumnInfo("Container", JdbcType.GUID, 0, false)); + columns.add(new BaseColumnInfo("id", JdbcType.VARCHAR, 0, false)); + columns.add(new BaseColumnInfo("s", JdbcType.VARCHAR, 30, true)); + columns.get(columns.size()-1).setKeyField(true); + columns.add(new BaseColumnInfo("i", JdbcType.INTEGER, 0, true)); + columns.add(new BaseColumnInfo("entityid", JdbcType.GUID, 0, false)); + _simpleTable = new TempTableInfo(TABLE, (List)(List)columns, Arrays.asList("s")); + + new SqlExecutor(_simpleTable.getSchema()).execute("CREATE TABLE " + _simpleTable.getSelectName() + + "(Container VARCHAR(255) NOT NULL, id VARCHAR(10) NOT NULL, s VARCHAR(32), i INTEGER, entityid VARCHAR(36))"); + _simpleTable.track(); + } + + @After + public void dropTable() + { + if (null != _simpleTable) + _simpleTable.delete(); + } + + private TableResultSet selectValues() + { + return new SqlSelector(_simpleTable.getSchema(), "SELECT Container, id, s, i, entityid FROM " + _simpleTable + " ORDER BY id").getResultSet(); + } + + private Map row(String s, Integer i) + { + Map map = new CaseInsensitiveHashMap<>(); + map.put("s", s); + map.put("i", i); + return map; + } + + @Test + public void mergeTest() throws Exception + { + Container c = JunitUtil.getTestContainer(); + + Collection cols = List.of( + new ImportableColumn("s", "s", "VARCHAR(32)", true), + new ImportableColumn("i", "i", "INTEGER", false) + ); + + ListofMapsDataIterator values = new ListofMapsDataIterator( + new LinkedHashSet<>(List.of("s","i")), + List.of( + row("Bob", 100), + row("Sally", 200), + row(null, 300)) + ); + + SpecimenImporter importer = new SpecimenImporter(c, null); // TODO: don't have user here + final Integer[] counter = new Integer[] { 0 }; + ComputedColumn idCol = new ComputedColumn() + { + @Override + public String getName() { return "id"; } + @Override + public Object getValue(Map row) + { + return String.valueOf(++counter[0]); + } + }; + + // Insert rows + Pair, Integer> pair = importer.mergeTable(_simpleTable.getSchema(), _simpleTable.getSelectName(), _simpleTable, cols, values, idCol, true); + assertNotNull(pair); + assertEquals(pair.first.size(), cols.size()); + assertEquals(3, pair.second.intValue()); + assertEquals(3, counter[0].intValue()); + + + String bobGUID, sallyGUID, nullGUID, jimmyGUID; + int jimmyID; + + try (TableResultSet rs = selectValues()) + { + Iterator> iter = rs.iterator(); + Map row0 = iter.next(); + Map row1 = iter.next(); + Map row2 = iter.next(); + assertFalse(iter.hasNext()); + + assertEquals("Bob", row0.get("s")); + assertEquals(100, row0.get("i")); + assertEquals("1", row0.get("id")); + bobGUID = (String)row0.get("entityid"); +// assertNotNull(bobGUID); + + assertEquals("Sally", row1.get("s")); + assertEquals(200, row1.get("i")); + assertEquals("2", row1.get("id")); + sallyGUID = (String)row1.get("entityid"); +// assertNotNull(sallyGUID); + + assertEquals(null, row2.get("s")); + assertEquals(300, row2.get("i")); + assertEquals("3", row2.get("id")); + nullGUID = (String)row2.get("entityid"); +// assertNotNull(nullGUID); + } + + // Add one new row, update one existing row. + values = new ListofMapsDataIterator( + new LinkedHashSet<>(List.of("s","i")), + List.of( + row("Bob", 105), + row(null, 305), + row("Jimmy", 405) + ) + ); + + + pair = importer.mergeTable(_simpleTable.getSchema(), _simpleTable.getSelectName(), _simpleTable, cols, values, idCol, true); + assertEquals(pair.first.size(), cols.size()); + assertEquals(3, pair.second.intValue()); +// assertEquals(4, counter[0].intValue()); + + try (TableResultSet rs = selectValues()) + { + Iterator> iter = rs.iterator(); + Map row0 = iter.next(); + Map row1 = iter.next(); + Map row2 = iter.next(); + Map row3 = iter.next(); + assertFalse(iter.hasNext()); + + assertEquals("Bob", row0.get("s")); + assertEquals(105, row0.get("i")); + assertEquals("1", row0.get("id")); + assertEquals(bobGUID, row0.get("entityid")); + + assertEquals("Sally", row1.get("s")); + assertEquals(200, row1.get("i")); + assertEquals("2", row1.get("id")); + assertEquals(sallyGUID, row1.get("entityid")); + + assertEquals(null, row2.get("s")); + assertEquals(305, row2.get("i")); + assertEquals("3", row2.get("id")); + assertEquals(nullGUID, row2.get("entityid")); + + assertEquals("Jimmy", row3.get("s")); + assertEquals(405, row3.get("i")); + + jimmyID = Integer.valueOf((String) row3.get("id")); + assertTrue(4 <= jimmyID); + jimmyGUID = (String)row3.get("entityid"); + + // HMM, the original mergeTable() fails this check (non DataIteratyor) + // assertNotNull(jimmyGUID); + } + + // let's really mix things up and try updating using a column that's not marked as the PK + + Collection colsAlternate = List.of( + new ImportableColumn("s", "s", "VARCHAR(32)", false), + new ImportableColumn("i", "i", "INTEGER", true) + ); + + values = new ListofMapsDataIterator( + new LinkedHashSet<>(List.of("s","i")), + List.of( + row("John", 405) + ) + ); + + pair = importer.mergeTable(_simpleTable.getSchema(), _simpleTable.getSelectName(), _simpleTable, colsAlternate, values, idCol, true); + assertEquals(pair.first.size(), cols.size()); + assertEquals(1, pair.second.intValue()); + + try (TableResultSet rs = selectValues()) + { + Iterator> iter = rs.iterator(); + Map row0 = iter.next(); + Map row1 = iter.next(); + Map row2 = iter.next(); + Map row3 = iter.next(); + assertFalse(iter.hasNext()); + + assertEquals("John", row3.get("s")); + assertEquals(405, row3.get("i")); + assertEquals(jimmyID, (int)Integer.valueOf((String)row3.get("id"))); + assertEquals(jimmyGUID, row3.get("entityid")); + } + } + + @Test + public void tempTableConsistencyTest() + { + Container c = JunitUtil.getTestContainer(); + DbSchema schema = SpecimenSchema.get().getSchema(); + User user = TestContext.get().getUser(); + + // Provisioned specimen tables need to be created in this order + TableInfo specimenTableInfo = SpecimenSchema.get().getTableInfoSpecimen(c, user); + TableInfo vialTableInfo = SpecimenSchema.get().getTableInfoVial(c, user); + TableInfo specimenEventTableInfo = SpecimenSchema.get().getTableInfoSpecimenEvent(c, user); + SpecimenImporter importer = new SpecimenImporter(c, user); + + for (SpecimenColumn specimenColumn : importer._specimenColumns) + { + TargetTable targetTable = specimenColumn.getTargetTable(); + List tableNames = targetTable.getTableNames(); + for (String tableName : tableNames) + { + TableInfo tableInfo = null; + if ("SpecimenEvent".equalsIgnoreCase(tableName)) + tableInfo = specimenEventTableInfo; + else if ("Specimen".equalsIgnoreCase(tableName)) + tableInfo = specimenTableInfo; + else if ("Vial".equalsIgnoreCase(tableName)) + tableInfo = vialTableInfo; + if (null != tableInfo) + checkConsistency(tableInfo, tableName, specimenColumn); + } + } + for (ImportableColumn importableColumn : SpecimenColumns.ADDITIVE_COLUMNS) + { + checkConsistency(schema, "SpecimenAdditive", importableColumn); + } + for (ImportableColumn importableColumn : SpecimenColumns.DERIVATIVE_COLUMNS) + { + checkConsistency(schema, "SpecimenDerivative", importableColumn); + } + for (ImportableColumn importableColumn : SpecimenColumns.PRIMARYTYPE_COLUMNS) + { + checkConsistency(schema, "SpecimenPrimaryType", importableColumn); + } + for (ImportableColumn importableColumn : SITE_COLUMNS) + { + checkConsistency(schema, "Site", importableColumn); + } + } + + private void checkConsistency(DbSchema schema, String tableName, ImportableColumn importableColumn) + { + TableInfo tableInfo = schema.getTable(tableName); + checkConsistency(tableInfo, tableName, importableColumn); + } + + private void checkConsistency(TableInfo tableInfo, String tableName, ImportableColumn importableColumn) + { + String columnName = importableColumn.getDbColumnName(); + ColumnInfo columnInfo = tableInfo.getColumn(columnName); + JdbcType jdbcType = columnInfo.getJdbcType(); + + if (jdbcType == JdbcType.VARCHAR) + { + assert importableColumn.getJdbcType() == JdbcType.VARCHAR: + "Column '" + columnName + "' in table '" + tableName + "' has inconsistent types in SQL and importer: varchar vs " + importableColumn.getJdbcType().name(); + assert columnInfo.getScale() == importableColumn.getMaxSize() : + "Column '" + columnName + "' in table '" + tableName + "' has inconsistent varchar lengths in importer and SQL: " + importableColumn.getMaxSize() + " vs " + columnInfo.getScale(); + } + assert jdbcType == importableColumn.getJdbcType() || + (importableColumn.getJdbcType() == JdbcType.DOUBLE && (jdbcType == JdbcType.REAL || jdbcType == JdbcType.DECIMAL)) : + "Column '" + columnName + "' in table '" + tableName + "' has inconsistent types in SQL and importer: " + columnInfo.getJdbcType() + " vs " + importableColumn.getJdbcType(); + } + } + + private int executeSQL(DbSchema schema, CharSequence sql, Object... params) + { + return executeSQL(schema, new SQLFragment(sql, params)); + } + + private int executeSQL(DbSchema schema, SQLFragment sql) + { + debug(sql); + return new SqlExecutor(schema).execute(sql); + } +} diff --git a/specimen/src/org/labkey/specimen/importer/SpecimenSettingsImporter.java b/specimen/src/org/labkey/specimen/importer/SpecimenSettingsImporter.java index 4fd362a0..c568389b 100644 --- a/specimen/src/org/labkey/specimen/importer/SpecimenSettingsImporter.java +++ b/specimen/src/org/labkey/specimen/importer/SpecimenSettingsImporter.java @@ -20,8 +20,6 @@ import org.labkey.api.data.Container; import org.labkey.api.security.GroupManager; import org.labkey.api.security.SecurityManager; -import org.labkey.api.specimen.SpecimenRequestStatus; -import org.labkey.api.specimen.importer.RequestabilityManager; import org.labkey.api.specimen.location.LocationCache; import org.labkey.api.specimen.location.LocationImpl; import org.labkey.api.specimen.settings.DisplaySettings; @@ -35,6 +33,7 @@ import org.labkey.security.xml.GroupType; import org.labkey.specimen.SpecimenRequestManager; import org.labkey.specimen.SpecimenRequestManager.SpecimenRequestInput; +import org.labkey.specimen.SpecimenRequestStatus; import org.labkey.specimen.actions.ManageReqsBean; import org.labkey.specimen.model.SpecimenRequestActor; import org.labkey.specimen.requirements.RequirementType; diff --git a/specimen/src/org/labkey/specimen/importer/SpecimenTableType.java b/specimen/src/org/labkey/specimen/importer/SpecimenTableType.java new file mode 100644 index 00000000..e150702c --- /dev/null +++ b/specimen/src/org/labkey/specimen/importer/SpecimenTableType.java @@ -0,0 +1,32 @@ +package org.labkey.specimen.importer; + +import java.util.Collection; + +public class SpecimenTableType +{ + private final String _name; + private final String _tableName; + private final Collection _columns; + + public SpecimenTableType(String name, String tableName, Collection columns) + { + _name = name; + _tableName = tableName; + _columns = columns; + } + + public String getName() + { + return _name; + } + + public String getTableName() + { + return _tableName; + } + + public Collection getColumns() + { + return _columns; + } +} diff --git a/specimen/src/org/labkey/specimen/importer/StandardSpecimenImportStrategy.java b/specimen/src/org/labkey/specimen/importer/StandardSpecimenImportStrategy.java new file mode 100644 index 00000000..b3027823 --- /dev/null +++ b/specimen/src/org/labkey/specimen/importer/StandardSpecimenImportStrategy.java @@ -0,0 +1,60 @@ +/* + * 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.specimen.importer; + +import org.labkey.api.data.Container; +import org.labkey.api.data.Filter; +import org.labkey.api.study.SpecimenImportStrategy; + +import java.util.Map; + +/** +* User: adam +* Date: 5/19/13 +* Time: 4:48 PM +*/ +public class StandardSpecimenImportStrategy implements SpecimenImportStrategy +{ + private final Container _c; + + public StandardSpecimenImportStrategy(Container c) + { + _c = c; + } + + @Override + public org.labkey.api.util.Filter> getImportFilter() + { + return null; + } + + @Override + public Filter getDeleteFilter() + { + return null; + } + + @Override + public Filter getInsertFilter() + { + return null; + } + + @Override + public void close() + { + } +} diff --git a/specimen/src/org/labkey/specimen/importer/TargetTable.java b/specimen/src/org/labkey/specimen/importer/TargetTable.java new file mode 100644 index 00000000..3a39031c --- /dev/null +++ b/specimen/src/org/labkey/specimen/importer/TargetTable.java @@ -0,0 +1,158 @@ +package org.labkey.specimen.importer; + +import java.util.ArrayList; +import java.util.List; + +public enum TargetTable +{ + SPECIMEN_EVENTS + { + @Override + public List getTableNames() + { + List names = new ArrayList<>(1); + names.add("SpecimenEvent"); + return names; + } + + @Override + public boolean isEvents() + { + return true; + } + + @Override + public boolean isSpecimens() + { + return false; + } + + @Override + public boolean isVials() + { + return false; + } + }, + SPECIMENS + { + @Override + public List getTableNames() + { + List names = new ArrayList<>(1); + names.add("Specimen"); + return names; + } + + @Override + public boolean isEvents() + { + return false; + } + + @Override + public boolean isSpecimens() + { + return true; + } + + @Override + public boolean isVials() + { + return false; + } + }, + VIALS + { + @Override + public List getTableNames() + { + List names = new ArrayList<>(1); + names.add("Vial"); + return names; + } + + @Override + public boolean isEvents() + { + return false; + } + + @Override + public boolean isSpecimens() + { + return false; + } + + @Override + public boolean isVials() + { + return true; + } + }, + SPECIMENS_AND_SPECIMEN_EVENTS + { + @Override + public List getTableNames() + { + List names = new ArrayList<>(1); + names.add("Specimen"); + names.add("SpecimenEvent"); + return names; + } + + @Override + public boolean isEvents() + { + return true; + } + + @Override + public boolean isSpecimens() + { + return true; + } + + @Override + public boolean isVials() + { + return false; + } + }, + VIALS_AND_SPECIMEN_EVENTS + { + @Override + public List getTableNames() + { + List names = new ArrayList<>(1); + names.add("Vial"); + names.add("SpecimenEvent"); + return names; + } + + @Override + public boolean isEvents() + { + return true; + } + + @Override + public boolean isSpecimens() + { + return false; + } + + @Override + public boolean isVials() + { + return true; + } + }; + + public abstract boolean isEvents(); + + public abstract boolean isVials(); + + public abstract boolean isSpecimens(); + + public abstract List getTableNames(); +} diff --git a/specimen/src/org/labkey/specimen/notifications/DefaultRequestNotification.java b/specimen/src/org/labkey/specimen/notifications/DefaultRequestNotification.java index b838346b..c11d67e1 100644 --- a/specimen/src/org/labkey/specimen/notifications/DefaultRequestNotification.java +++ b/specimen/src/org/labkey/specimen/notifications/DefaultRequestNotification.java @@ -25,7 +25,7 @@ import org.labkey.api.data.ExcelWriter; import org.labkey.api.data.TSVWriter; import org.labkey.api.specimen.Vial; -import org.labkey.api.specimen.query.SpecimenQueryView; +import org.labkey.specimen.query.SpecimenQueryView; import org.labkey.api.view.ViewContext; import org.labkey.specimen.model.SpecimenRequestEvent; import org.labkey.specimen.requirements.SpecimenRequest; diff --git a/specimen/src/org/labkey/specimen/query/BaseSpecimenQueryView.java b/specimen/src/org/labkey/specimen/query/BaseSpecimenQueryView.java new file mode 100644 index 00000000..e6f80e40 --- /dev/null +++ b/specimen/src/org/labkey/specimen/query/BaseSpecimenQueryView.java @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2008-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.specimen.query; + +import org.jetbrains.annotations.Nullable; +import org.labkey.api.data.ButtonBar; +import org.labkey.api.data.DataRegion; +import org.labkey.api.data.SimpleFilter; +import org.labkey.api.data.Sort; +import org.labkey.api.query.QuerySettings; +import org.labkey.api.query.QueryView; +import org.labkey.api.query.UserSchema; +import org.labkey.api.study.CohortFilter; +import org.labkey.api.view.ActionURL; +import org.labkey.api.view.DataView; +import org.labkey.api.view.DisplayElement; + +import java.util.List; + +public abstract class BaseSpecimenQueryView extends QueryView +{ + protected final SimpleFilter _filter; + protected final Sort _sort; + protected @Nullable CohortFilter _cohortFilter; + private List _buttons; + + public BaseSpecimenQueryView(UserSchema schema, QuerySettings settings, SimpleFilter filter, Sort sort) + { + super(schema, settings); + _filter = filter; + _sort = sort; + } + + @Override + public DataView createDataView() + { + DataView view = super.createDataView(); + SimpleFilter filter = (SimpleFilter) view.getRenderContext().getBaseFilter(); + if (filter != null) + filter.addAllClauses(_filter); + else + filter = _filter; + + if (_cohortFilter != null) + _cohortFilter.addFilterCondition(view.getTable(), getContainer(), filter); + + view.getRenderContext().setBaseFilter(filter); + Sort sort = view.getRenderContext().getBaseSort(); + if (sort == null) + { + view.getRenderContext().setBaseSort(_sort); + } + else + { + sort.insertSort(_sort); + } + if (_buttons != null) + { + ButtonBar bbar = new ButtonBar(); + bbar.addAll(view.getDataRegion().getButtonBar(DataRegion.MODE_GRID).getList()); + bbar.addAll(_buttons); + view.getDataRegion().setShowRecordSelectors(true); + view.getDataRegion().setButtonBar(bbar); + } + view.getDataRegion().setButtonBarPosition(getButtonBarPosition()); + return view; + } + + protected DataRegion.ButtonBarPosition getButtonBarPosition() + { + return DataRegion.ButtonBarPosition.TOP; + } + + public void setButtons(List buttons) + { + _buttons = buttons; + } + + public ActionURL getBaseViewURL() + { + return urlBaseView(); + } +} diff --git a/specimen/src/org/labkey/specimen/query/SpecimenEventQueryView.java b/specimen/src/org/labkey/specimen/query/SpecimenEventQueryView.java index 95d8cadb..4eeb8f6d 100644 --- a/specimen/src/org/labkey/specimen/query/SpecimenEventQueryView.java +++ b/specimen/src/org/labkey/specimen/query/SpecimenEventQueryView.java @@ -23,7 +23,6 @@ import org.labkey.api.query.UserSchema; import org.labkey.api.specimen.SpecimenQuerySchema; import org.labkey.api.specimen.Vial; -import org.labkey.api.specimen.query.BaseSpecimenQueryView; import org.labkey.api.study.Study; import org.labkey.api.study.StudyService; import org.labkey.api.view.ViewContext; diff --git a/specimen/src/org/labkey/specimen/query/SpecimenQueryView.java b/specimen/src/org/labkey/specimen/query/SpecimenQueryView.java new file mode 100644 index 00000000..c0be0a59 --- /dev/null +++ b/specimen/src/org/labkey/specimen/query/SpecimenQueryView.java @@ -0,0 +1,1044 @@ +/* + * Copyright (c) 2008-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.specimen.query; + +import org.apache.commons.lang3.StringUtils; +import org.jetbrains.annotations.NotNull; +import org.labkey.api.collections.ResultSetRowMapFactory; +import org.labkey.api.data.AbstractTableInfo; +import org.labkey.api.data.Aggregate; +import org.labkey.api.data.ColumnHeaderType; +import org.labkey.api.data.ColumnInfo; +import org.labkey.api.data.Container; +import org.labkey.api.data.ContainerManager; +import org.labkey.api.data.DataRegion; +import org.labkey.api.data.DetailsColumn; +import org.labkey.api.data.DisplayColumn; +import org.labkey.api.data.MenuButton; +import org.labkey.api.data.RenderContext; +import org.labkey.api.data.Results; +import org.labkey.api.data.RuntimeSQLException; +import org.labkey.api.data.SQLFragment; +import org.labkey.api.data.SimpleDisplayColumn; +import org.labkey.api.data.SimpleFilter; +import org.labkey.api.data.Sort; +import org.labkey.api.data.SqlSelector; +import org.labkey.api.data.Table; +import org.labkey.api.data.TableInfo; +import org.labkey.api.query.DetailsURL; +import org.labkey.api.query.FieldKey; +import org.labkey.api.query.QueryParam; +import org.labkey.api.query.QuerySettings; +import org.labkey.api.query.QueryView; +import org.labkey.api.query.UserSchema; +import org.labkey.api.reports.report.ReportUrls; +import org.labkey.api.security.User; +import org.labkey.api.specimen.SpecimenQuerySchema; +import org.labkey.api.specimen.SpecimenSchema; +import org.labkey.api.specimen.Vial; +import org.labkey.api.specimen.security.permissions.EditSpecimenDataPermission; +import org.labkey.api.specimen.security.permissions.RequestSpecimensPermission; +import org.labkey.api.specimen.settings.DisplaySettings; +import org.labkey.api.specimen.settings.RepositorySettings; +import org.labkey.api.specimen.settings.SettingsManager; +import org.labkey.api.study.CohortFilter; +import org.labkey.api.study.Study; +import org.labkey.api.study.StudyService; +import org.labkey.api.study.StudyUtils; +import org.labkey.api.study.model.ParticipantDataset; +import org.labkey.api.util.HtmlString; +import org.labkey.api.util.HtmlStringBuilder; +import org.labkey.api.util.PageFlowUtil; +import org.labkey.api.view.ActionURL; +import org.labkey.api.view.DataView; +import org.labkey.api.view.ViewContext; +import org.labkey.specimen.actions.SpecimenController; + +import java.io.IOException; +import java.io.Writer; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static java.util.Objects.requireNonNull; + +public class SpecimenQueryView extends BaseSpecimenQueryView +{ + public enum Mode + { + COMMENTS, + REQUESTS, + DEFAULT + } + + private final boolean _requireSequenceNum; + + private static class SpecimenDataRegion extends DataRegion + { + private final Map _requiredColumns = new HashMap<>(); + + @Override + protected boolean isErrorRow(RenderContext ctx, int rowIndex) + { + return StudyUtils.isFieldTrue(ctx, "QualityControlFlag"); + } + + protected void addColumnIfMissing(Set currentColumns, String colName) + { + ColumnInfo col = getTable().getColumn(colName); + if (col == null) + throw new IllegalStateException("Failed to find expected column " + colName); + ColumnInfo foundCol = null; + for (Iterator it = currentColumns.iterator(); it.hasNext() && foundCol == null; ) + { + ColumnInfo current = it.next(); + if (colName.equalsIgnoreCase(current.getName())) + foundCol = current; + } + if (foundCol == null) + { + currentColumns.add(col); + foundCol = col; + } + _requiredColumns.put(colName, foundCol); + } + + @Override + public void addQueryColumns(Set columns) + { + for (String requiredColumn : getRequiredColumns()) + addColumnIfMissing(columns, requiredColumn); + } + + protected List getRequiredColumns() + { + List cols = new ArrayList<>(); + cols.add("QualityControlFlag"); + return cols; + } + + protected String getRequiredColumnAlias(String columnName) + { + ColumnInfo col = _requiredColumns.get(columnName); + if (col == null) + throw new IllegalStateException("Failed to find expected column " + columnName); + return col.getAlias(); + } + + protected Object getRequiredColumnValue(RenderContext ctx, String columnName) + { + return ctx.getRow().get(getRequiredColumnAlias(columnName)); + } + } + + private static class VialRestrictedDataRegion extends SpecimenDataRegion + { + @Override + protected boolean isRecordSelectorEnabled(RenderContext ctx) + { + return isAvailable(ctx); + } + + @Override + protected List getRequiredColumns() + { + List required = super.getRequiredColumns(); + required.add("GlobalUniqueId"); + required.add("Available"); + required.add("AvailabilityReason"); + return required; + } + + @Override + protected String getRecordSelectorId(RenderContext ctx) + { + return "check_" + getRequiredColumnValue(ctx, "GlobalUniqueId"); + } + + @Override + protected void renderExtraRecordSelectorContent(RenderContext ctx, Writer out) throws IOException + { + if (!isAvailable(ctx)) + { + String reasonAlias = getRequiredColumnAlias("AvailabilityReason"); + Object reason = ctx.getRow().get(reasonAlias); + + HtmlStringBuilder builder = HtmlStringBuilder.of(reason instanceof String r ? r : "Specimen Unavailable.") + .append(HtmlString.BR).append(HtmlString.BR) + .append("Click ") + .append(PageFlowUtil.link("[history]", getHistoryLink(ctx)).clearClasses()) + .append(" for more information."); + + PageFlowUtil.popupHelp(builder.getHtmlString(), "Specimen Unavailable").appendTo(out); + } + } + + private ActionURL getHistoryLink(RenderContext ctx) + { + String containerId = (String) ctx.getRow().get("Container"); + ActionURL url = getHistoryLinkURL(ctx.getViewContext(), containerId); + Long specimenId = (Long) ctx.getRow().get("RowId"); + + return url.addParameter("id", specimenId); + } + + private boolean isAvailable(RenderContext ctx) + { + return StudyUtils.isFieldTrue(ctx, getRequiredColumnAlias("Available")); + } + } + + protected static ActionURL getHistoryLinkURL(ViewContext ctx, String containerId) + { + Container container = null != containerId ? ContainerManager.getForId(containerId) : ctx.getContainer(); + return new ActionURL(SpecimenController.SpecimenEventsAction.class, container).addReturnURL(ctx.getActionURL()); + } + + private class SpecimenRestrictedDataRegion extends SpecimenDataRegion + { + @Override + protected boolean isRecordSelectorEnabled(RenderContext ctx) + { + return isAvailable(ctx); + } + + @Override + protected List getRequiredColumns() + { + List required = super.getRequiredColumns(); + required.add("SpecimenHash"); + required.add("AvailableCount"); + return required; + } + + @Override + protected String getRecordSelectorId(RenderContext ctx) + { + return "check_" + getRequiredColumnValue(ctx, "SpecimenHash"); + } + + @Override + protected void renderExtraRecordSelectorContent(RenderContext ctx, Writer out) throws IOException + { + if (!isAvailable(ctx)) + { + PageFlowUtil.popupHelp(HtmlString.of("No vials of this primary specimen are available. All " + + "vials are either part of an active specimen request, locked by an administrator, or not " + + "currently held by a repository."), "Specimens Unavailable").appendTo(out); + } + } + + private boolean isAvailable(RenderContext ctx) + { + Integer count; + if (getRequiredColumnValue(ctx, "AvailableCount") != null) + { + count = ((Number) getRequiredColumnValue(ctx, "AvailableCount")).intValue(); + } + else + { + String hash = (String) getRequiredColumnValue(ctx, "SpecimenHash"); + count = getSampleCounts(ctx).get(hash); + } + return (count != null && count.intValue() > 0); + } + } + + private boolean _showHistoryLinks; + private boolean _disableLowVialIndicators; + private boolean _showRecordSelectors; + private boolean _restrictRecordSelectors = true; + private Map _hiddenFormFields; + // key of _availableSpecimenCounts is a specimen hash, which includes ptid, type, etc. + private Map _availableSpecimenCounts; + + private final boolean _participantVisitFiltered; + private final ViewType _viewType; + + + public enum ViewType + { + SUMMARY() + { + @Override + public String getQueryName() + { + return "SpecimenSummary"; + } + + @Override + public String getViewName() + { + return null; + } + + @Override + public boolean isVialView() + { + return false; + } + + @Override + public boolean isForExport() + { + return false; + } + }, + VIALS() + { + @Override + public String getQueryName() + { + return "SpecimenDetail"; + } + + @Override + public String getViewName() + { + return null; + } + + @Override + public boolean isVialView() + { + return true; + } + + @Override + public boolean isForExport() + { + return false; + } + }, + VIALS_EMAIL() + { + @Override + public String getQueryName() + { + return "SpecimenDetail"; + } + + @Override + public String getViewName() + { + return "SpecimenEmail"; + } + + @Override + public boolean isVialView() + { + return true; + } + + @Override + public boolean isForExport() + { + return true; + } + }; + + public abstract String getQueryName(); + + public abstract String getViewName(); + + public abstract boolean isVialView(); + + public abstract boolean isForExport(); + } + + protected SpecimenQueryView(UserSchema schema, QuerySettings settings, SimpleFilter filter, Sort sort, ViewType viewType, + boolean participantVisitFiltered, CohortFilter cohortFilter, boolean requireSequenceNum) + { + super(schema, settings, filter, sort); + _viewType = viewType; + _cohortFilter = cohortFilter; + _participantVisitFiltered = participantVisitFiltered; + _requireSequenceNum = requireSequenceNum; + + RepositorySettings repositorySettings = SettingsManager.get().getRepositorySettings(schema.getContainer()); + boolean isEditable = ViewType.VIALS == viewType && repositorySettings.isSpecimenDataEditable() && getContainer().hasPermission(getUser(), EditSpecimenDataPermission.class); + setShowUpdateColumn(isEditable); + + setShowInsertNewButton(isEditable); + setShowDeleteButton(isEditable); + setShowImportDataButton(false); + if (isEditable) + { + AbstractTableInfo tableInfo = (AbstractTableInfo) getTable(); + ActionURL updateUrl = new ActionURL(SpecimenController.UpdateSpecimenQueryRowAction.class, getContainer()); + updateUrl.addParameter("schemaName", "study"); + updateUrl.addParameter(QueryView.DATAREGIONNAME_DEFAULT + "." + QueryParam.queryName, tableInfo.getName()); + setUpdateURL(new DetailsURL(updateUrl, Collections.singletonMap("RowId", "RowId"))); + + // we want a DetailsURL-like string so clear the container + ActionURL insertActionURL = new ActionURL(SpecimenController.InsertSpecimenQueryRowAction.class, ContainerManager.getRoot()); + insertActionURL.addParameter("schemaName", "study"); + insertActionURL.addParameter(QueryView.DATAREGIONNAME_DEFAULT + "." + QueryParam.queryName, tableInfo.getName()); + + setInsertURL(insertActionURL.getLocalURIString(false)); + } + + setViewItemFilter((type, label) -> + StudyUtils.STUDY_CROSSTAB_REPORT_TYPE.equals(type) || DEFAULT_ITEM_FILTER.accept(type, label) + ); + } + + public static SpecimenQueryView createView(ViewContext context, ViewType viewType) + { + SimpleFilter filter = new SimpleFilter(); + return createView(context, filter, createDefaultSort(viewType), viewType, false, null, false); + } + + public static SpecimenQueryView createView(ViewContext context, QuerySettings settings, ViewType viewType) + { + SimpleFilter filter = new SimpleFilter(); + return createView(context, settings, filter, createDefaultSort(viewType), viewType, false, null, false); + } + + public static SpecimenQueryView createView(ViewContext context, ViewType viewType, CohortFilter cohortFilter) + { + SimpleFilter filter = new SimpleFilter(); + return createView(context, filter, createDefaultSort(viewType), viewType, false, cohortFilter, false); + } + + public static SpecimenQueryView createView(ViewContext context, List samples, ViewType viewType) + { + SimpleFilter filter = new SimpleFilter(); + addFilterClause(filter, samples, viewType); + return createView(context, filter, createDefaultSort(viewType), viewType, false, null, false); + } + + public static SpecimenQueryView createView(ViewContext context, @NotNull Collection participantDatasets, ViewType viewType) + { + SimpleFilter filter = new SimpleFilter(); + Study study = StudyService.get().getStudy(context.getContainer()); + addFilterClause(requireNonNull(study), filter, participantDatasets); + return createView(context, filter, createDefaultSort(viewType), viewType, true, null, true); + } + + public enum PARAMS + { + excludeRequestedBySite, + showCompleteRequestedByEnrollmentSite, + showCompleteRequestedBySite, + showCompleteRequestedOnly, + showNotCompleteRequestedOnly, + showNotRequestedOnly, + showRequestedByEnrollmentSite, + showRequestedBySite, + showRequestedOnly, + } + + private static boolean hasRequestFilterParam(ViewContext context, PARAMS param) + { + String paramValue = StringUtils.trimToNull((String) context.get(param.name())); + return null != paramValue && paramValue.equalsIgnoreCase("true"); + } + + private static void addOnlyPreviouslyRequestedFilter(ViewContext context, SimpleFilter filter) + { + String showRequestedBySite = StringUtils.trimToNull((String) context.get(PARAMS.showRequestedBySite.name())); + if (null != showRequestedBySite) + addOnlyPreviouslyRequestedClause(filter, context.getContainer(), Integer.parseInt(showRequestedBySite), false); + else + { + showRequestedBySite = StringUtils.trimToNull((String) context.get(PARAMS.showCompleteRequestedBySite.name())); + if (null != showRequestedBySite) + addOnlyPreviouslyRequestedClause(filter, context.getContainer(), Integer.parseInt(showRequestedBySite), true); + } + } + + private static void addEnrollmentSiteRequestFilter(ViewContext context, SimpleFilter filter) + { + String showRequestedByEnrollmentSite = StringUtils.trimToNull((String) context.get(PARAMS.showRequestedByEnrollmentSite.name())); + if (null != showRequestedByEnrollmentSite) + addPreviouslyRequestedEnrollmentClause(filter, context.getContainer(), context.getUser(), Integer.parseInt(showRequestedByEnrollmentSite), false); + else + { + showRequestedByEnrollmentSite = StringUtils.trimToNull((String) context.get(PARAMS.showCompleteRequestedByEnrollmentSite.name())); + if (null != showRequestedByEnrollmentSite) + addPreviouslyRequestedEnrollmentClause(filter, context.getContainer(), context.getUser(), Integer.parseInt(showRequestedByEnrollmentSite), true); + } + } + + private static void addRequestFilter(ViewContext context, SimpleFilter filter) + { + if (hasRequestFilterParam(context, PARAMS.showRequestedOnly)) + addPreviouslyRequestedClause(filter, context.getContainer(), true, false); + else if (hasRequestFilterParam(context, PARAMS.showCompleteRequestedOnly)) + addPreviouslyRequestedClause(filter, context.getContainer(), true, true); + else if (hasRequestFilterParam(context, PARAMS.showNotRequestedOnly)) + addPreviouslyRequestedClause(filter, context.getContainer(), false, false); + else if (hasRequestFilterParam(context, PARAMS.showNotCompleteRequestedOnly)) + addPreviouslyRequestedClause(filter, context.getContainer(), false, true); + } + + private static SpecimenQueryView createView(ViewContext context, SimpleFilter filter, Sort sort, ViewType viewType, + boolean participantVisitFiltered, CohortFilter cohortFilter, boolean requireSequenceNum) + { + Study study = StudyService.get().getStudy(context.getContainer()); + UserSchema schema = SpecimenQuerySchema.get(study, context.getUser()); + String queryName = viewType.getQueryName(); + QuerySettings qs = schema.getSettings(context, queryName, queryName); + return createView(context, qs, filter, sort, viewType, participantVisitFiltered, cohortFilter, requireSequenceNum); + } + + private static SpecimenQueryView createView(ViewContext context, QuerySettings qs, SimpleFilter filter, Sort sort, ViewType viewType, + boolean participantVisitFiltered, CohortFilter cohortFilter, boolean requireSequenceNum) + { + Study study = StudyService.get().getStudy(context.getContainer()); + UserSchema schema = SpecimenQuerySchema.get(study, context.getUser()); + String viewName = viewType.getViewName(); + if (qs.getViewName() == null && viewName != null) + qs.setViewName(viewName); + + String excludeRequested = StringUtils.trimToNull((String) context.get(PARAMS.excludeRequestedBySite.name())); + if (null != excludeRequested) + addNotPreviouslyRequestedClause(filter, context.getContainer(), Integer.parseInt(excludeRequested)); + addOnlyPreviouslyRequestedFilter(context, filter); + addEnrollmentSiteRequestFilter(context, filter); + addRequestFilter(context, filter); + return new SpecimenQueryView(schema, qs, filter, sort, viewType, participantVisitFiltered, cohortFilter, requireSequenceNum); + } + + public Map getSampleCounts(RenderContext ctx) + { + if (null == _availableSpecimenCounts) + try + { + _availableSpecimenCounts = new HashMap<>(); + ResultSet rs = ctx.getResults(); + Set specimenHashes = new HashSet<>(); + int originalRow = rs.getRow(); + rs.last(); + if (rs.getRow() - originalRow < 1000) + { + rs.absolute(originalRow); + if (rs.getRow() == 0) + { + rs.next(); + } + do + { + specimenHashes.add(rs.getString("SpecimenHash")); + if (specimenHashes.size() > 150) + { + _availableSpecimenCounts.putAll(getSpecimenCounts(ctx.getContainer(), specimenHashes)); + specimenHashes = new HashSet<>(); + } + } + while (rs.next()); + if (!specimenHashes.isEmpty()) + { + _availableSpecimenCounts.putAll(getSpecimenCounts(ctx.getContainer(), specimenHashes)); + } + } + else + { + _availableSpecimenCounts.putAll(getSpecimenCounts(ctx.getContainer(), null)); + } + rs.absolute(originalRow); + } + catch (SQLException e) + { + throw new RuntimeSQLException(e); + } + + return _availableSpecimenCounts; + } + + private Map getSpecimenCounts(Container container, Collection specimenHashes) + { + TableInfo tableInfoSpecimen = SpecimenSchema.get().getTableInfoSpecimen(container); + + SQLFragment extraClause = null; + if (specimenHashes != null) + { + extraClause = new SQLFragment(" WHERE SpecimenHash "); + tableInfoSpecimen.getSqlDialect().appendInClauseSql(extraClause, specimenHashes); + } + + final Map map = new HashMap<>(); + + SQLFragment sql = new SQLFragment("SELECT SpecimenHash, CAST(AvailableCount AS Integer) AS AvailableCount FROM "); + sql.append(tableInfoSpecimen.getFromSQL("")); + if (extraClause != null) + { + sql.append(extraClause); + } + new SqlSelector(SpecimenSchema.get().getSchema(), sql).forEach(rs -> { + String specimenHash = rs.getString("SpecimenHash"); + map.put(specimenHash, rs.getInt("AvailableCount")); + }); + + return map; + } + + private static Sort createDefaultSort(ViewType viewType) + { + if (viewType.isVialView()) + return new Sort("GlobalUniqueId"); + else + return new Sort(requireNonNull(StudyService.get()).getSubjectColumnName(getContextContainer()) + ",Visit,PrimaryType,DerivativeType,AdditiveType"); + } + + protected static SimpleFilter addFilterClause(SimpleFilter filter, List vials, ViewType viewType) + { + if (vials != null && vials.size() > 0) + { + StringBuilder whereClause = new StringBuilder(); + if (viewType.isVialView()) + whereClause.append("RowId IN ("); + else + whereClause.append("SpecimenHash IN ("); + for (int i = 0; i < vials.size(); i++) + { + if (viewType.isVialView()) + whereClause.append(vials.get(i).getRowId()); + else + whereClause.append(vials.get(i).getSpecimenHash()); + if (i < vials.size() - 1) + whereClause.append(", "); + } + whereClause.append(")"); + filter = filter.addWhereClause(whereClause.toString(), null); + } + return filter; + } + + protected static SimpleFilter addFilterClause(Study study, SimpleFilter filter, @NotNull Collection participantDatasets) + { + boolean visitBased = study.getTimepointType().isVisitBased(); + if (!participantDatasets.isEmpty()) + { + StringBuilder whereClause = new StringBuilder(); + List params = new ArrayList<>(); + String sep = ""; + for (ParticipantDataset pd : participantDatasets) + { + whereClause.append(sep); + whereClause.append("("); + if (visitBased && pd.getSequenceNum() != null) + { + whereClause.append("SequenceNum = ? AND "); + params.add(pd.getSequenceNum()); + } + else if (pd.getVisitDate() != null) + { + whereClause.append(SpecimenSchema.get().getSqlDialect().getDateTimeToDateCast("DrawTimestamp")); + whereClause.append(" = "); + whereClause.append(SpecimenSchema.get().getSqlDialect().getDateTimeToDateCast("?")).append(" AND "); + params.add(pd.getVisitDate()); + } + whereClause.append(requireNonNull(StudyService.get()).getSubjectColumnName(getContextContainer())).append(" = ?)"); + params.add(pd.getParticipantId()); + sep = " OR "; + } + filter = filter.addWhereClause(whereClause.toString(), params.toArray(new Object[params.size()])); + } + return filter; + } + + protected static SimpleFilter addNotPreviouslyRequestedClause(SimpleFilter filter, Container container, int locationId) + { + TableInfo tableInfoVial = SpecimenSchema.get().getTableInfoVial(container); + SQLFragment sql = new SQLFragment("SpecimenHash NOT IN (" + + "SELECT v.SpecimenHash from study.SampleRequestSpecimen rs join study.SampleRequest r on rs.SamplerequestId=r.RowId " + + "join "); + sql.append(tableInfoVial.getFromSQL("v")).append(" on rs.SpecimenGlobalUniqueId=v.GlobalUniqueId " + + "join study.SamplerequestStatus status ON r.StatusId=status.RowId " + + "where r.DestinationSiteId=").appendValue(locationId).append(" AND status.SpecimensLocked=").append(tableInfoVial.getSqlDialect().getBooleanTRUE()).append(")"); + + assert(0 == sql.getParams().size()); + + filter.addWhereClause(sql.getSQL(), null, FieldKey.fromParts("SpecimenHash")); + + return filter; + } + + protected static SimpleFilter addOnlyPreviouslyRequestedClause(SimpleFilter filter, Container container, int locationId, boolean completedRequestsOnly) + { + String sql = "GlobalUniqueId IN (" + + "SELECT rs.SpecimenGlobalUniqueId FROM study.SampleRequestSpecimen rs join study.SampleRequest r on rs.SamplerequestId=r.RowId " + + "join study.SamplerequestStatus status ON r.StatusId=status.RowId " + + "where r.DestinationSiteId=? AND rs.Container=? AND status.SpecimensLocked=?" + + (completedRequestsOnly ? " AND status.FinalState=?" : "") + ")"; + + Object[] params; + if (completedRequestsOnly) + params = new Object[]{locationId, container.getId(), Boolean.TRUE, Boolean.TRUE}; + else + params = new Object[]{locationId, container.getId(), Boolean.TRUE}; + + filter.addWhereClause(sql, params, FieldKey.fromParts("GlobalUniqueId")); + + return filter; + } + + protected static SimpleFilter addPreviouslyRequestedEnrollmentClause(SimpleFilter filter, Container container, User user, int locationId, boolean completedRequestsOnly) + { + SQLFragment sql = getBaseRequestedEnrollmentSql(container, user, completedRequestsOnly); + + if (locationId == -1) + { + sql.append("IS NULL)"); + } + else + { + sql.append("= ?)"); + sql.add(locationId); + } + + filter.addWhereClause(sql, FieldKey.fromParts("GlobalUniqueId")); + + return filter; + } + + public static SQLFragment getBaseRequestedEnrollmentSql(Container container, User user, boolean isCompleteRequestsOnly) + { + UserSchema schema = SpecimenQuerySchema.get(StudyService.get().getStudy(container), user); + TableInfo tableInfoSpecimenDetail = schema.getTable(SpecimenQuerySchema.SPECIMEN_WRAP_TABLE_NAME); + if (null == tableInfoSpecimenDetail) + throw new IllegalStateException("SpecimenDetail table not found."); + String tableInfoAlias = "Specimen"; + + SQLFragment baseSql = new SQLFragment("GlobalUniqueId IN (SELECT Specimen.GlobalUniqueId FROM "); + baseSql.append(tableInfoSpecimenDetail.getFromSQL(tableInfoAlias)).append(",\n" + + "study.SampleRequestSpecimen AS RequestSpecimen,\n" + + "study.SampleRequest AS Request, study.SampleRequestStatus AS Status,\n" + + "study.Participant AS Participant\n" + + "WHERE Request.Container = Status.Container AND\n" + + " Request.StatusId = Status.RowId AND\n" + + " RequestSpecimen.SampleRequestId = Request.RowId AND\n" + + " RequestSpecimen.Container = Request.Container AND\n" + + " Specimen.Container = RequestSpecimen.Container AND\n" + + " Specimen.GlobalUniqueId = RequestSpecimen.SpecimenGlobalUniqueId AND\n" + + " Participant.Container = Specimen.Container AND\n" + + " Participant.ParticipantId = Specimen.Ptid AND\n" + + " Status.SpecimensLocked = ? AND\n" + + (isCompleteRequestsOnly ? " Status.FinalState = ? AND\n" : "") + + " Specimen.Container = ? AND\n" + + " Participant.EnrollmentSiteId "); + baseSql.add(Boolean.TRUE); + if (isCompleteRequestsOnly) + baseSql.add(Boolean.TRUE); + baseSql.add(container); + return baseSql; + } + + protected static SimpleFilter addPreviouslyRequestedClause(SimpleFilter filter, Container container, boolean showRequested, boolean completedRequestsOnly) + { + String sql = "GlobalUniqueId " + (showRequested ? "" : "NOT ") + "IN (" + + "SELECT rs.SpecimenGlobalUniqueId FROM study.SampleRequestSpecimen rs join study.SampleRequest r on rs.SamplerequestId=r.RowId " + + "join study.SamplerequestStatus status ON r.StatusId=status.RowId " + + "where rs.Container=? AND status.SpecimensLocked=?" + (completedRequestsOnly ? " AND status.FinalState=?" : "") + + ")"; + Object[] params; + if (completedRequestsOnly) + params = new Object[]{container.getId(), Boolean.TRUE, Boolean.TRUE}; + else + params = new Object[]{container.getId(), Boolean.TRUE}; + + filter.addWhereClause(sql, params, FieldKey.fromParts("GlobalUniqueId")); + + return filter; + } + + @Override + protected DataRegion createDataRegion() + { + SpecimenDataRegion rgn; + if (_viewType.isVialView()) + { + if (_showRecordSelectors && _restrictRecordSelectors) + rgn = new VialRestrictedDataRegion(); + else + rgn = new SpecimenDataRegion(); + rgn.setRecordSelectorValueColumns("RowId"); + + // NOTE: Setting here instead of in table ctor because if set when SpecimenDetail is sub-query, we have too many columns in sub-query (Dave) +// if (ViewType.VIALS.equals(_viewType)) +// ((BaseColumnInfo)getTable().getColumn("Container")).setRequired(true); + } + else + { + if (_showRecordSelectors && _restrictRecordSelectors) + rgn = new SpecimenRestrictedDataRegion(); + else + rgn = new SpecimenDataRegion(); + + rgn.setRecordSelectorValueColumns("SpecimenHash"); + if (_showRecordSelectors) + { + if (null == _hiddenFormFields) + _hiddenFormFields = new HashMap<>(); + _hiddenFormFields.put("fromGroupedView", Boolean.TRUE.toString()); + } + } + configureDataRegion(rgn); + rgn.prepareDisplayColumns(getViewContext().getContainer()); + return rgn; + } + + @Override + protected void configureDataRegion(DataRegion rgn) + { + super.configureDataRegion(rgn); + + DisplayColumn rowIdCol = rgn.getDisplayColumn("RowId"); + if (rowIdCol != null) + rowIdCol.setVisible(false); + DisplayColumn containerIdCol = rgn.getDisplayColumn("Container"); + if (containerIdCol != null) + containerIdCol.setVisible(false); + + rgn.setShowRecordSelectors(_showRecordSelectors); + + if (_hiddenFormFields != null) + { + for (Map.Entry param : _hiddenFormFields.entrySet()) + rgn.addHiddenFormField(param.getKey(), param.getValue()); + } + + rgn.addHiddenFormField("referrer", getViewContext().getActionURL().getLocalURIString()); + + if (_viewType.isVialView()) + { + addAggregateIfInDisplay("Volume", Aggregate.BaseType.SUM); + addAggregateIfInDisplay("GlobalUniqueId", Aggregate.BaseType.COUNT); + } + else + { + addAggregateIfInDisplay("TotalVolume", Aggregate.BaseType.SUM); + addAggregateIfInDisplay("LockedInRequestCount", Aggregate.BaseType.SUM); + addAggregateIfInDisplay("AtRepositoryCount", Aggregate.BaseType.SUM); + addAggregateIfInDisplay("VialCount", Aggregate.BaseType.SUM); + addAggregateIfInDisplay("AvailableVolume", Aggregate.BaseType.SUM); + addAggregateIfInDisplay("AvailableCount", Aggregate.BaseType.SUM); + } + } + + // Add the aggregate clause only if the aggregated column is going to be displayed + // (otherwise SQL is not happy) + private void addAggregateIfInDisplay(String colName, Aggregate.Type aggregateType) + { + ColumnInfo columnInfo = getTable().getColumn(colName); + if (columnInfo != null) + { + for (DisplayColumn displayColumn : super.getDisplayColumns()) + { + if (displayColumn instanceof DetailsColumn) + continue; + + if (columnInfo.equals(displayColumn.getColumnInfo())) + { + getSettings().addAggregates(new Aggregate(columnInfo, aggregateType)); + return; + } + } + } + } + + @Override + public List getDisplayColumns() + { + List cols = super.getDisplayColumns(); + + if (_viewType.isForExport()) + { + // Remove rowId column and update column, if present + DisplayColumn column = cols.get(0); + if (column.getName().equalsIgnoreCase("rowid") || column.getName().equalsIgnoreCase("update")) + cols.remove(0); + column = cols.get(0); + if (column.getName().equalsIgnoreCase("rowid") || column.getName().equalsIgnoreCase("update")) + cols.remove(0); + } + + if (_viewType.isVialView()) + { + if (_showHistoryLinks) + { + cols.add(0, new SimpleDisplayColumn("[history]") + { + @Override + public String renderURL(RenderContext ctx) + { + String containerId = (String) ctx.getRow().get("Container"); + Long specimenId = (Long) ctx.getRow().get("RowId"); + ActionURL url = getHistoryLinkURL(ctx.getViewContext(), containerId).addParameter("id", specimenId.toString()).addParameter("selected", _participantVisitFiltered); + return url.toString(); + } + }); + } + } + + boolean oneVialIndicator = false; + boolean zeroVialIndicator = false; + if (!_disableLowVialIndicators) + { + DisplaySettings settings = SettingsManager.get().getDisplaySettings(getContainer()); + oneVialIndicator = settings.getLastVialEnum() == DisplaySettings.DisplayOption.ALL_USERS || + (settings.getLastVialEnum() == DisplaySettings.DisplayOption.ADMINS_ONLY && + getUser().hasRootAdminPermission()); + zeroVialIndicator = settings.getZeroVialsEnum() == DisplaySettings.DisplayOption.ALL_USERS || + (settings.getZeroVialsEnum() == DisplaySettings.DisplayOption.ADMINS_ONLY && + getUser().hasRootAdminPermission()); + } + RepositorySettings settings = SettingsManager.get().getRepositorySettings(getContainer()); + if (settings.isEnableRequests() && !_viewType.isForExport() && getViewContext().getContainer().hasPermission(getUser(), RequestSpecimensPermission.class)) + { + // Only add this column if we're using advanced specimen management and not exported to email or attachment + cols.add(0, new SpecimenRequestDisplayColumn(this, getTable(), zeroVialIndicator, oneVialIndicator, + SettingsManager.get().isSpecimenShoppingCartEnabled(getContainer()) && _showRecordSelectors)); + } + + // this column is normally hidden but we need it on the select for any specimen filters + if (_requireSequenceNum) + { + ColumnInfo seqNumCol = getTable().getColumn("SequenceNum"); + if (seqNumCol != null) + cols.add(seqNumCol.getRenderer()); + } + + return cols; + } + + public boolean isShowingVials() + { + return _viewType.isVialView(); + } + + public boolean isShowHistoryLinks() + { + return _showHistoryLinks; + } + + public void addHiddenFormField(String name, String value) + { + if (_hiddenFormFields == null) + _hiddenFormFields = new HashMap<>(); + _hiddenFormFields.put(name, value); + } + + public boolean isDisableLowVialIndicators() + { + return _disableLowVialIndicators; + } + + public void setDisableLowVialIndicators(boolean disableLowVialIndicators) + { + _disableLowVialIndicators = disableLowVialIndicators; + } + + public boolean isShowRecordSelectors() + { + return _showRecordSelectors; + } + + public boolean isRestrictRecordSelectors() + { + return _restrictRecordSelectors; + } + + public void setRestrictRecordSelectors(boolean restrictRecordSelectors) + { + _restrictRecordSelectors = restrictRecordSelectors; + } + + public void setShowHistoryLinks(boolean showHistoryLinks) + { + _showHistoryLinks = showHistoryLinks; + } + + @Override + public void setShowRecordSelectors(boolean showRecordSelectors) + { + //assert !(showRecordSelectors && !_detailsView) : "Only details view may show record selectors"; + _showRecordSelectors = showRecordSelectors; + } + + public String getSimpleHtmlTable() throws SQLException, IOException + { + getSettings().setMaxRows(Table.ALL_ROWS); + DataView view = createDataView(); + DataRegion rgn = view.getDataRegion(); + RenderContext renderContext = view.getRenderContext(); + + try (Results results = rgn.getResults(renderContext)) + { + List allColumns = getExportColumns(rgn.getDisplayColumns()); + List columns = new ArrayList<>(); + for (DisplayColumn col : allColumns) + { + if (col.shouldRender(renderContext)) + columns.add(col); + } + StringBuilder builder = new StringBuilder(); + builder.append("\n \n"); + for (DisplayColumn col : columns) + { + String header = PageFlowUtil.filter(col.getCaption()); + header = header.replaceAll(" ", " "); + builder.append(" \n"); + } + builder.append(" \n"); + int row = 0; + + ResultSetRowMapFactory factory = ResultSetRowMapFactory.create(results); + + while (results.next()) + { + renderContext.setRow(factory.getRowMap(results)); + builder.append(" \n"); + builder.append(" \n"); + for (DisplayColumn col : columns) + { + HtmlString value = col.getFormattedHtml(renderContext); + builder.append(" \n"); + } + builder.append(" \n"); + } + builder.append("
 ").append(header).append("
").append(row).append("").append(value).append("
"); + return builder.toString(); + } + } + + @Override + public void addManageViewItems(MenuButton button, Map params) + { + ActionURL url = requireNonNull(PageFlowUtil.urlProvider(ReportUrls.class)).urlManageViews(getViewContext().getContainer()); + for (Map.Entry entry : params.entrySet()) + url.addParameter(entry.getKey(), entry.getValue()); + + button.addMenuItem("Manage Views", url); + } + + @Override + protected ColumnHeaderType getColumnHeaderType() + { + return ColumnHeaderType.Caption; + } +} diff --git a/specimen/src/org/labkey/specimen/query/SpecimenRequestDisplayColumn.java b/specimen/src/org/labkey/specimen/query/SpecimenRequestDisplayColumn.java new file mode 100644 index 00000000..531ba316 --- /dev/null +++ b/specimen/src/org/labkey/specimen/query/SpecimenRequestDisplayColumn.java @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2009-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.specimen.query; + +import org.labkey.api.data.ColumnInfo; +import org.labkey.api.data.RenderContext; +import org.labkey.api.data.SimpleDisplayColumn; +import org.labkey.api.data.TableInfo; +import org.labkey.api.study.StudyUtils; +import org.labkey.api.util.HtmlString; +import org.labkey.api.util.PageFlowUtil; + +import java.io.IOException; +import java.io.Writer; +import java.util.Set; + +public class SpecimenRequestDisplayColumn extends SimpleDisplayColumn +{ + private final boolean _showOneVialIndicator; + private final boolean _showZeroVialIndicator; + private final TableInfo _table; + private final boolean _showCartLinks; + private final SpecimenQueryView _specimenQueryView; + + public SpecimenRequestDisplayColumn(SpecimenQueryView specimenQueryView, TableInfo table, boolean showOneVialIndicator, + boolean showZeroVialIndicator, boolean showCartLinks) + { + _specimenQueryView = specimenQueryView; + _showOneVialIndicator = showOneVialIndicator; + _showZeroVialIndicator = showZeroVialIndicator; + _table = table; + _showCartLinks = showCartLinks; + } + + @Override + public void renderGridCellContents(RenderContext ctx, Writer out) throws IOException + { + String hash = (String) ctx.getRow().get("SpecimenHash"); + String globalUniqueId = (String) ctx.getRow().get("GlobalUniqueId"); + int count = -1; + if (ctx.getRow().get("AvailableCount") != null) + { + count = ((Number)ctx.getRow().get("AvailableCount")).intValue(); + } + else + { + Integer c = _specimenQueryView.getSampleCounts(ctx).get(hash); + if (c != null) + count = c.intValue(); + } + + boolean vialView = _specimenQueryView.isShowingVials(); + boolean available = (!vialView && count > 0) || (vialView && StudyUtils.isFieldTrue(ctx, "Available")); + boolean showCart = _showCartLinks && available; + String script = null; + out.write("
"); + if (showCart) + { + script = globalUniqueId == null ? "requestByHash('" + hash + "'); return false;" + : "requestByGlobalUniqueId('" + globalUniqueId + "'); return false;"; + out.write(""); + } + + if (_showOneVialIndicator && count == 1) + { + out.write(getVialCountHtml(ctx, "", + "One Vial Available", "Only one vial of this primary specimen is available.", script)); + } + else if (_showZeroVialIndicator && count == 0) + { + out.write(getVialCountHtml(ctx, "", + "Zero Vials Available", "No vials of this primary specimen are currently available for request.", null)); + } + else + { + out.write(getVialCountHtml(ctx, "
" + String.valueOf(count) + "
", + count + " Vials Available", count + " vials of this primary specimen are currently available for new requests.", script)); + } + + if (showCart) + out.write("
"); + out.write("
"); + } + + private String getVialCountHtml(RenderContext ctx, String cellHtml, String popupTitle, String popupBody, String requestScript) + { + StringBuilder builder = new StringBuilder(); + if (requestScript != null) + { + cellHtml += ""; + popupBody += "

Click the shopping cart icon to request this specimen."; + } + builder.append(PageFlowUtil.popupHelp(HtmlString.unsafe(popupBody), popupTitle).link(HtmlString.unsafe(cellHtml)).script(requestScript)); + return builder.toString(); + } + + @Override + public void addQueryColumns(Set set) + { + set.add(_table.getColumn("SpecimenHash")); + // fix for https://cpas.fhcrc.org/Issues/home/issues/details.view?issueId=3116 + ColumnInfo atRepositoryColumn = _table.getColumn("AtRepository"); + if (atRepositoryColumn != null) + set.add(atRepositoryColumn); + ColumnInfo lockedInRequestColumn = _table.getColumn("LockedInRequest"); + if (lockedInRequestColumn != null) + set.add(lockedInRequestColumn); + ColumnInfo availableCountColumn = _table.getColumn("AvailableCount"); + if (availableCountColumn != null) + set.add(availableCountColumn); + ColumnInfo availableColumn = _table.getColumn("Available"); + if (availableColumn != null) + set.add(availableColumn); + } +} \ No newline at end of file diff --git a/specimen/src/org/labkey/specimen/query/SpecimenRequestQueryView.java b/specimen/src/org/labkey/specimen/query/SpecimenRequestQueryView.java index 87f0a443..8384b1bb 100644 --- a/specimen/src/org/labkey/specimen/query/SpecimenRequestQueryView.java +++ b/specimen/src/org/labkey/specimen/query/SpecimenRequestQueryView.java @@ -29,8 +29,6 @@ import org.labkey.api.query.UserSchema; import org.labkey.api.security.User; import org.labkey.api.specimen.SpecimenQuerySchema; -import org.labkey.api.specimen.SpecimenRequestStatus; -import org.labkey.api.specimen.query.BaseSpecimenQueryView; import org.labkey.api.specimen.security.permissions.RequestSpecimensPermission; import org.labkey.api.specimen.settings.SettingsManager; import org.labkey.api.study.Study; @@ -41,6 +39,7 @@ import org.labkey.api.view.NavTree; import org.labkey.api.view.ViewContext; import org.labkey.specimen.SpecimenRequestManager; +import org.labkey.specimen.SpecimenRequestStatus; import org.labkey.specimen.actions.SpecimenController.DeleteRequestAction; import org.labkey.specimen.actions.SpecimenController.ManageRequestAction; import org.labkey.specimen.actions.SpecimenController.SubmitRequestAction; diff --git a/specimen/src/org/labkey/specimen/query/SpecimenUpdateService.java b/specimen/src/org/labkey/specimen/query/SpecimenUpdateService.java new file mode 100644 index 00000000..7d3f9682 --- /dev/null +++ b/specimen/src/org/labkey/specimen/query/SpecimenUpdateService.java @@ -0,0 +1,541 @@ +/* + * 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.specimen.query; + +import org.apache.commons.beanutils.converters.LongConverter; +import org.apache.logging.log4j.Logger; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.labkey.api.data.ColumnInfo; +import org.labkey.api.data.Container; +import org.labkey.api.data.SQLFragment; +import org.labkey.api.data.SimpleFilter; +import org.labkey.api.data.SqlSelector; +import org.labkey.api.data.TableInfo; +import org.labkey.api.data.TableSelector; +import org.labkey.api.dataiterator.DataIteratorBuilder; +import org.labkey.api.dataiterator.DataIteratorContext; +import org.labkey.api.query.AbstractQueryUpdateService; +import org.labkey.api.query.BatchValidationException; +import org.labkey.api.query.FieldKey; +import org.labkey.api.query.InvalidKeyException; +import org.labkey.api.query.QueryService; +import org.labkey.api.query.QueryUpdateServiceException; +import org.labkey.api.query.ValidationException; +import org.labkey.api.security.User; +import org.labkey.api.security.permissions.DeletePermission; +import org.labkey.api.specimen.SpecimenEvent; +import org.labkey.api.specimen.SpecimenEventManager; +import org.labkey.api.specimen.SpecimenSchema; +import org.labkey.api.specimen.Vial; +import org.labkey.api.study.Study; +import org.labkey.api.study.StudyService; +import org.labkey.api.util.UnexpectedException; +import org.labkey.api.view.UnauthorizedException; +import org.labkey.specimen.SpecimenManager; +import org.labkey.specimen.SpecimenRequestException; +import org.labkey.specimen.SpecimenRequestManager; +import org.labkey.specimen.importer.EditableSpecimenImporter; + +import java.io.IOException; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public class SpecimenUpdateService extends AbstractQueryUpdateService +{ + Logger _logger = null; + + public SpecimenUpdateService(TableInfo queryTable) + { + super(queryTable); + } + + + @Override + public int importRows(User user, Container container, DataIteratorBuilder rows, BatchValidationException errors, Map configParameters, @Nullable Map extraScriptContext) + { + if (null != configParameters) + { + Object o = configParameters.get(ConfigParameters.Logger); + if (o instanceof Logger) + _logger = (Logger)o; + } + DataIteratorContext context = getDataIteratorContext(errors, InsertOption.IMPORT, configParameters); + return _importRowsUsingInsertRows(user, container, rows.getDataIterator(context), errors, extraScriptContext); + } + + + @Override + public List> deleteRows(User user, Container container, List> keys, @Nullable Map configParameters, @Nullable Map extraScriptContext) + throws BatchValidationException + { + if (!hasPermission(user, DeletePermission.class)) + throw new UnauthorizedException("You do not have permission to delete data from this table."); + + BatchValidationException errors = new BatchValidationException(); + errors.setExtraContext(extraScriptContext); + getQueryTable().fireBatchTrigger(container, user, TableInfo.TriggerType.DELETE, true, errors, extraScriptContext); + + Set rowIds = new HashSet<>(keys.size()); + for (Map key : keys) + { + Object rowId = key.get("rowid"); + if (null != rowId) + rowIds.add(Long.class == rowId.getClass() ? (Long) rowId : (Integer) rowId); + else + throw new IllegalArgumentException("RowId not found for a row."); + } + List vials = SpecimenManager.get().getVials(container, user, rowIds); + if (vials.size() != keys.size()) + throw new IllegalStateException("Specimens should be same size as rows."); + + List> rows = new ArrayList<>(1); + try + { + for (Vial vial : vials) + checkDeletability(container, vial); + + for (Vial vial : vials) + SpecimenManager.get().deleteSpecimen(vial, false); + + SpecimenRequestManager.get().clearCaches(container); + + // Force recalculation of requestability and specimen table + EditableSpecimenImporter importer = new EditableSpecimenImporter(container, user, false); + importSpecimens(importer, rows, true); + } + catch (ValidationException e) + { + errors.addRowError(e); + throw errors; + } + catch (SpecimenRequestException e) + { + errors.addRowError(new ValidationException(e.getMessage())); + throw errors; + } + catch (IOException e) + { + throw new IllegalStateException(e.getMessage()); + } + + getQueryTable().fireBatchTrigger(container, user, TableInfo.TriggerType.DELETE, false, errors, extraScriptContext); + + addAuditEvent(user, container, QueryService.AuditAction.DELETE, configParameters, null, null); + + return new ArrayList<>(); + } + + @Override + protected Map deleteRow(User user, Container container, Map oldRow) + throws InvalidKeyException, ValidationException + { + long rowId = keyFromMap(oldRow); + Vial vial = SpecimenManager.get().getVial(container, user, rowId); + + if (null == vial) + throw new IllegalArgumentException("No specimen found for rowId: " + rowId); + + checkDeletability(container, vial); + SpecimenManager.get().deleteSpecimen(vial, true); + List> rows = new ArrayList<>(1); + + try + { + // Force recalculation of requestability and specimen table + EditableSpecimenImporter importer = new EditableSpecimenImporter(container, user, false); + importSpecimens(importer, rows, true); + } + catch (IOException e) + { + throw new IllegalStateException(e.getMessage()); + } + + return oldRow; + } + + @Override + @SuppressWarnings("unchecked") + protected Map getRow(User user, Container container, Map keys) + throws InvalidKeyException + { + Map row = new TableSelector(getQueryTable()).getObject(keyFromMap(keys), Map.class); + Map result = new HashMap<>(); + for (ColumnInfo column : getQueryTable().getColumns()) + if (row.containsKey(column.getName())) + result.put(column.getName(), row.get(column.getName())); + else + result.put(column.getName(), row.get(column.getName().toLowerCase())); + + return result; + } + + @Override + public List> insertRows(User user, Container container, List> rows, BatchValidationException errors, @Nullable Map configParameters, Map extraScriptContext) + { + Study study = StudyService.get().getStudy(container); + if (null == study) + throw new IllegalStateException("No study found."); + + boolean hasTableScript = getQueryTable().hasTriggers(container); + + errors.setExtraContext(extraScriptContext); + try + { + if (hasTableScript) + getQueryTable().fireBatchTrigger(container, user, TableInfo.TriggerType.INSERT, true, errors, extraScriptContext); + } + catch (BatchValidationException e) + { + return null; + } + + List> result = new ArrayList<>(rows.size()); + String subjectColumnName = study.getSubjectColumnName(); + + try + { + for (Map row : rows) + { + if (null == row.get(subjectColumnName) || null == row.get("SequenceNum")) + { + throw new ValidationException(subjectColumnName + " and Sequence Num are required."); + } + + if (!subjectColumnName.equalsIgnoreCase("participantid")) + { + row.put("participantid", row.get(subjectColumnName)); + row.remove(subjectColumnName); + } + + row.put("externalid", 1); + } + + EditableSpecimenImporter importer = new EditableSpecimenImporter(container, user, true); + importSpecimens(importer, rows, true); + } + catch (IOException e) + { + throw new IllegalStateException(e.getMessage()); + } + catch (IllegalStateException e) + { + errors.addRowError(new ValidationException(e.getMessage())); + } + catch (ValidationException e) + { + errors.addRowError(e); + } + + if (!errors.hasErrors()) + { +/* try + { + result = getRows(user, container, keys); + } + catch (InvalidKeyException e) + { + throw UnexpectedException.wrap(e); + } +*/ + } + + try + { + if (hasTableScript) + getQueryTable().fireBatchTrigger(container, user, TableInfo.TriggerType.INSERT, false, errors, extraScriptContext); + } + catch (BatchValidationException e) + { + + } + + return result; + } + + @Override + protected Map insertRow(User user, Container container, Map row) + throws ValidationException + { + Study study = StudyService.get().getStudy(container); + if (null == study) + throw new IllegalStateException("No study found."); + String subjectColumnName = study.getSubjectColumnName(); + + if (null == row.get(subjectColumnName) || null == row.get("SequenceNum")) + { + throw new ValidationException(subjectColumnName + " and Sequence Num are required."); + } + + if (!subjectColumnName.equalsIgnoreCase("participantid")) + { + row.put("participantid", row.get(subjectColumnName)); + row.remove(subjectColumnName); + } + + row.put("externalid", 1); + List> rows = new ArrayList<>(1); + rows.add(row); + + try + { + EditableSpecimenImporter importer = new EditableSpecimenImporter(container, user, true); + importSpecimens(importer, rows, true); + } + catch (IOException e) + { + throw new IllegalStateException(e.getMessage()); + } + catch (IllegalStateException e) + { + throw new ValidationException(e.getMessage()); + } + +/* + try + { + return getRow(user, container, keys.get(0)); + } + catch (InvalidKeyException e) + { + throw UnexpectedException.wrap(e); + } +*/ + return new HashMap<>(); + } + + @Override + public List> updateRows(User user, Container container, List> rows, List> oldKeys, BatchValidationException errors, @Nullable Map configParameters, Map extraScriptContext) + throws InvalidKeyException, BatchValidationException, QueryUpdateServiceException, SQLException + { + if (oldKeys != null && rows.size() != oldKeys.size()) + throw new IllegalArgumentException("rows and oldKeys are required to be the same length, but were " + rows.size() + " and " + oldKeys + " in length, respectively"); + + errors.setExtraContext(extraScriptContext); + getQueryTable().fireBatchTrigger(container, user, TableInfo.TriggerType.UPDATE, true, errors, extraScriptContext); + + Set rowIds = new HashSet<>(rows.size()); + Map> uniqueRows = new HashMap<>(rows.size()); + for (int i = 0; i < rows.size(); i++) + { + Map row = rows.get(i); + Map keys = oldKeys == null ? row : oldKeys.get(i); + long rowId = keyFromMap(keys); + rowIds.add(rowId); + uniqueRows.put(rowId, row); + } + Map vials = new HashMap<>(); + for (Vial vial : SpecimenManager.get().getVials(container, user, rowIds)) + { + vials.put(vial.getRowId(), vial); + } + if (vials.size() != uniqueRows.size()) + throw new IllegalStateException("Specimens should be same size as rows."); + + try + { + for (Vial vial : vials.values()) + checkEditability(container, vial); + } + catch (ValidationException e) + { + errors.addRowError(e); + throw errors; + } + catch (SpecimenRequestException e) + { + errors.addRowError(new ValidationException(e.getMessage())); + throw errors; + } + + EditableSpecimenImporter importer = new EditableSpecimenImporter(container, user, false); + List> newKeys = new ArrayList<>(uniqueRows.size()); + List> newRows = new ArrayList<>(uniqueRows.size()); + long externalId = SpecimenManager.get().getMaxExternalId(container) + 1; + for (Map.Entry> row : uniqueRows.entrySet()) + { + Vial vial = vials.get(row.getKey()); + Map rowMap = prepareRowMap(container, row.getValue(), vial, importer, externalId++); + newRows.add(rowMap); + + Map keys = new HashMap<>(); + keys.put("rowId", row.getKey()); + newKeys.add(keys); + } + + try + { + importSpecimens(importer, newRows, true); + } + catch (ValidationException e) + { + e.fillIn(getQueryTable().getPublicSchemaName(), getQueryTable().getName(), newRows.get(0), 0); + errors.addRowError(e); + throw errors; + } + catch (IOException e) + { + throw new IllegalStateException(e.getMessage()); + } + + getQueryTable().fireBatchTrigger(container, user, TableInfo.TriggerType.UPDATE, false, errors, extraScriptContext); + + addAuditEvent(user, container, QueryService.AuditAction.UPDATE, configParameters, rows, null); + + return getRows(user, container, newKeys); + } + + @Override + protected Map updateRow(User user, Container container, Map row, @NotNull Map oldRow, @Nullable Map configParameters) + throws InvalidKeyException, ValidationException + { + long rowId = oldRow != null ? keyFromMap(oldRow) : keyFromMap(row); + Vial vial = SpecimenManager.get().getVial(container, user, rowId); + if (vial == null) + throw new IllegalArgumentException("No specimen found for rowId: " + rowId); + + checkEditability(container, vial); + + EditableSpecimenImporter importer = new EditableSpecimenImporter(container, user, false); + long externalId = SpecimenManager.get().getMaxExternalId(container) + 1; + Map rowMap = prepareRowMap(container, row, vial, importer, externalId); + + // TODO: this is a hack to best deal with Requestable being Null until a better fix can be accomplished + if (null == oldRow || null == oldRow.get("requestable")) + { + Object obj = rowMap.get("requestable"); + if (null != obj && !(Boolean)obj) + rowMap.put("requestable", null); + } + + List> rows = new ArrayList<>(1); + rows.add(rowMap); + + try + { + importSpecimens(importer, rows, true); + } + catch (IOException e) + { + throw new IllegalStateException(e.getMessage()); + } + + // The rowId will not have changed + Map keys = new HashMap<>(); + keys.put("rowId", vial.getRowId()); + try + { + return getRow(user, container, keys); + } + catch (InvalidKeyException e) + { + throw UnexpectedException.wrap(e); + } + } + + private long keyFromMap(Map map) throws InvalidKeyException + { + if (null == map) + throw new InvalidKeyException("No values provided"); + + Object rowId = map.get("rowId"); + if (null == rowId) + rowId = map.get("RowId"); + if (null == rowId) + rowId = map.get("rowid"); + if (null == rowId) + throw new InvalidKeyException("No value provided for 'rowId' column"); + Long rowLong = (Long)new LongConverter(null).convert(Long.class, rowId); + if (null == rowLong) + throw new InvalidKeyException("Unable to convert rowId of '" + rowId + "' to a long"); + return rowLong; + } + + private Map prepareRowMap(Container container, Map row, Vial vial, + EditableSpecimenImporter importer, long externalId) + { + // Get last event so that our input considers all fields that were set; + // Then overwrite whatever is coming from the form + Map rowMap = getLastEventMap(container, vial); + for (Map.Entry entry : row.entrySet()) + { + String columnName = importer.getMappedColumnName(entry.getKey()); + rowMap.put(columnName, entry.getValue()); + } + + rowMap.put("globaluniqueid", vial.getGlobalUniqueId()); + rowMap.put("ExternalId", externalId); + return rowMap; + } + + private void importSpecimens(EditableSpecimenImporter importer, List> rows, + boolean merge) throws IOException, ValidationException + { + rows = importer.mapColumnNamesToTsvColumnNames(rows); + importer.process(rows, merge, _logger); + } + + private void checkEditability(Container container, Vial vial) throws ValidationException + { + SQLFragment sql = new SQLFragment("SELECT COUNT(FinalState) FROM " + SpecimenSchema.get().getTableInfoSampleRequestStatus() + + " WHERE FinalState = " + SpecimenSchema.get().getSqlDialect().getBooleanFALSE()); + sql.append(" AND RowId IN (") + .append("SELECT StatusId FROM " + SpecimenSchema.get().getTableInfoSampleRequest() + " WHERE RowId IN (") + .append("SELECT SampleRequestId FROM " + SpecimenSchema.get().getTableInfoSampleRequestSpecimen() + " WHERE Container = ? "); + sql.add(container.getId()); + sql.append(" AND SpecimenGlobalUniqueId = ?").add(vial.getGlobalUniqueId()); + sql.append(")) GROUP BY FinalState"); + + ArrayList counts = new SqlSelector(getQueryTable().getSchema(), sql).getArrayList(Integer.class); + if (counts.size() > 1) + throw new IllegalStateException("Expected one and only one count of rows."); + else if (counts.size() > 0 && counts.get(0) != 0) + throw new ValidationException("Specimen may not be edited when it's in a non-final request."); + } + + private void checkDeletability(Container container, Vial vial) throws ValidationException + { + SQLFragment sql = new SQLFragment("SELECT COUNT(*) FROM " + SpecimenSchema.get().getTableInfoSampleRequestSpecimen() + " WHERE Container = ? "); + sql.add(container.getId()); + sql.append(" AND SpecimenGlobalUniqueId = ?").add(vial.getGlobalUniqueId()); + + ArrayList counts = new SqlSelector(getQueryTable().getSchema(), sql).getArrayList(Integer.class); + if (counts.size() > 1) + throw new ValidationException("Expected one and only one count of rows."); + else if (!counts.isEmpty() && counts.get(0) != 0) + throw new ValidationException("Specimen may not be deleted because it has been used in a request."); + } + + private static Map getLastEventMap(Container container, Vial vial) + { + List vials = Collections.singletonList(vial); + SpecimenEvent specimenEvent = SpecimenEventManager.get().getLastEvent(SpecimenManager.get().getSpecimenEvents(vials, false)); + if (null == specimenEvent) + throw new IllegalStateException("Expected at least one event for specimen."); + TableInfo tableInfoSpecimenEvent = SpecimenSchema.get().getTableInfoSpecimenEvent(container); + + SimpleFilter filter = new SimpleFilter(); + filter.addCondition(FieldKey.fromString("RowId"), specimenEvent.getRowId()); + return new TableSelector(tableInfoSpecimenEvent, filter, null).getMap(); + } +} + diff --git a/specimen/src/org/labkey/specimen/report/SpecimenVisitReport.java b/specimen/src/org/labkey/specimen/report/SpecimenVisitReport.java index 178a2898..1be3970b 100644 --- a/specimen/src/org/labkey/specimen/report/SpecimenVisitReport.java +++ b/specimen/src/org/labkey/specimen/report/SpecimenVisitReport.java @@ -24,7 +24,7 @@ import org.labkey.api.query.UserSchema; import org.labkey.api.security.User; import org.labkey.api.specimen.SpecimenQuerySchema; -import org.labkey.api.specimen.query.SpecimenQueryView; +import org.labkey.specimen.query.SpecimenQueryView; import org.labkey.api.study.CohortFilter; import org.labkey.api.study.Study; import org.labkey.api.study.StudyService; diff --git a/specimen/src/org/labkey/specimen/report/request/RequestEnrollmentLocationReport.java b/specimen/src/org/labkey/specimen/report/request/RequestEnrollmentLocationReport.java index 42f59458..a3155c52 100644 --- a/specimen/src/org/labkey/specimen/report/request/RequestEnrollmentLocationReport.java +++ b/specimen/src/org/labkey/specimen/report/request/RequestEnrollmentLocationReport.java @@ -16,7 +16,7 @@ package org.labkey.specimen.report.request; import org.labkey.api.data.SimpleFilter; -import org.labkey.api.specimen.query.SpecimenQueryView; +import org.labkey.specimen.query.SpecimenQueryView; import org.labkey.api.study.Visit; import org.labkey.specimen.report.SpecimenTypeVisitReport; import org.labkey.specimen.report.SpecimenVisitReportParameters; diff --git a/specimen/src/org/labkey/specimen/report/request/RequestEnrollmentSiteReportFactory.java b/specimen/src/org/labkey/specimen/report/request/RequestEnrollmentSiteReportFactory.java index 35d011a7..fe386095 100644 --- a/specimen/src/org/labkey/specimen/report/request/RequestEnrollmentSiteReportFactory.java +++ b/specimen/src/org/labkey/specimen/report/request/RequestEnrollmentSiteReportFactory.java @@ -21,7 +21,7 @@ import org.labkey.api.security.User; import org.labkey.api.specimen.location.LocationImpl; import org.labkey.api.specimen.location.LocationManager; -import org.labkey.api.specimen.query.SpecimenQueryView; +import org.labkey.specimen.query.SpecimenQueryView; import org.labkey.api.study.Location; import org.labkey.api.study.Visit; import org.labkey.api.util.HtmlString; diff --git a/specimen/src/org/labkey/specimen/report/request/RequestLocationReport.java b/specimen/src/org/labkey/specimen/report/request/RequestLocationReport.java index db9284b5..7b7c55f5 100644 --- a/specimen/src/org/labkey/specimen/report/request/RequestLocationReport.java +++ b/specimen/src/org/labkey/specimen/report/request/RequestLocationReport.java @@ -16,7 +16,7 @@ package org.labkey.specimen.report.request; import org.labkey.api.data.SimpleFilter; -import org.labkey.api.specimen.query.SpecimenQueryView; +import org.labkey.specimen.query.SpecimenQueryView; import org.labkey.api.study.Visit; import org.labkey.specimen.report.SpecimenTypeVisitReport; import org.labkey.specimen.report.SpecimenVisitReportParameters; diff --git a/specimen/src/org/labkey/specimen/report/request/RequestParticipantReport.java b/specimen/src/org/labkey/specimen/report/request/RequestParticipantReport.java index 75a4f2e3..54758b3d 100644 --- a/specimen/src/org/labkey/specimen/report/request/RequestParticipantReport.java +++ b/specimen/src/org/labkey/specimen/report/request/RequestParticipantReport.java @@ -16,7 +16,7 @@ package org.labkey.specimen.report.request; import org.labkey.api.data.SimpleFilter; -import org.labkey.api.specimen.query.SpecimenQueryView; +import org.labkey.specimen.query.SpecimenQueryView; import org.labkey.api.study.Visit; import org.labkey.api.view.ActionURL; import org.labkey.specimen.actions.SpecimenController; diff --git a/specimen/src/org/labkey/specimen/report/request/RequestReport.java b/specimen/src/org/labkey/specimen/report/request/RequestReport.java index 9af593e9..59c895b8 100644 --- a/specimen/src/org/labkey/specimen/report/request/RequestReport.java +++ b/specimen/src/org/labkey/specimen/report/request/RequestReport.java @@ -16,7 +16,7 @@ package org.labkey.specimen.report.request; import org.labkey.api.data.SimpleFilter; -import org.labkey.api.specimen.query.SpecimenQueryView; +import org.labkey.specimen.query.SpecimenQueryView; import org.labkey.api.study.Visit; import org.labkey.specimen.report.SpecimenTypeVisitReport; import org.labkey.specimen.report.SpecimenVisitReportParameters; diff --git a/specimen/src/org/labkey/specimen/settings/SettingsManager.java b/specimen/src/org/labkey/specimen/settings/SettingsManager.java index 6adfb2d8..f6bfdc43 100644 --- a/specimen/src/org/labkey/specimen/settings/SettingsManager.java +++ b/specimen/src/org/labkey/specimen/settings/SettingsManager.java @@ -4,13 +4,13 @@ import org.labkey.api.data.Container; import org.labkey.api.data.PropertyManager; import org.labkey.api.security.UserManager; -import org.labkey.api.specimen.SpecimenRequestStatus; import org.labkey.api.specimen.SpecimenSchema; import org.labkey.api.specimen.settings.DisplaySettings; import org.labkey.api.specimen.settings.RepositorySettings; import org.labkey.api.specimen.settings.StatusSettings; import org.labkey.api.study.QueryHelper; import org.labkey.specimen.SpecimenRequestManager; +import org.labkey.specimen.SpecimenRequestStatus; import java.util.List; import java.util.Map; diff --git a/specimen/src/org/labkey/specimen/view/NotificationBean.java b/specimen/src/org/labkey/specimen/view/NotificationBean.java index e9254b6f..cf156ce4 100644 --- a/specimen/src/org/labkey/specimen/view/NotificationBean.java +++ b/specimen/src/org/labkey/specimen/view/NotificationBean.java @@ -4,7 +4,6 @@ import org.labkey.api.attachments.Attachment; import org.labkey.api.data.Container; import org.labkey.api.security.User; -import org.labkey.api.specimen.SpecimenRequestStatus; import org.labkey.api.specimen.location.LocationManager; import org.labkey.api.study.Location; import org.labkey.api.study.SpecimenUrls; @@ -12,6 +11,7 @@ import org.labkey.api.view.ActionURL; import org.labkey.api.view.ViewContext; import org.labkey.specimen.SpecimenRequestManager; +import org.labkey.specimen.SpecimenRequestStatus; import org.labkey.specimen.model.SpecimenRequestEvent; import org.labkey.specimen.notifications.DefaultRequestNotification; diff --git a/specimen/src/org/labkey/specimen/view/configRequestabilityRules.jsp b/specimen/src/org/labkey/specimen/view/configRequestabilityRules.jsp index d21da96d..f162164e 100644 --- a/specimen/src/org/labkey/specimen/view/configRequestabilityRules.jsp +++ b/specimen/src/org/labkey/specimen/view/configRequestabilityRules.jsp @@ -16,10 +16,10 @@ */ %> <%@ page import="org.labkey.api.data.Container"%> -<%@ page import="org.labkey.api.specimen.importer.RequestabilityManager"%> <%@ page import="org.labkey.api.study.StudyUrls"%> -<%@ page import="org.labkey.api.view.ActionURL" %> +<%@ page import="org.labkey.api.view.ActionURL"%> <%@ page import="org.labkey.api.view.template.ClientDependencies" %> +<%@ page import="org.labkey.specimen.importer.RequestabilityManager" %> <%@ page extends="org.labkey.api.jsp.JspBase" %> <%@ taglib prefix="labkey" uri="http://www.labkey.org/taglib" %> <%! diff --git a/specimen/src/org/labkey/specimen/view/manageRequest.jsp b/specimen/src/org/labkey/specimen/view/manageRequest.jsp index 1fdc2d92..f8b3d780 100644 --- a/specimen/src/org/labkey/specimen/view/manageRequest.jsp +++ b/specimen/src/org/labkey/specimen/view/manageRequest.jsp @@ -20,10 +20,9 @@ <%@ page import="org.labkey.api.data.ContainerManager"%> <%@ page import="org.labkey.api.security.User"%> <%@ page import="org.labkey.api.security.UserManager"%> -<%@ page import="org.labkey.api.specimen.SpecimenRequestStatus"%> -<%@ page import="org.labkey.api.specimen.Vial" %> -<%@ page import="org.labkey.api.specimen.location.LocationImpl"%> -<%@ page import="org.labkey.api.specimen.location.LocationManager" %> +<%@ page import="org.labkey.api.specimen.Vial"%> +<%@ page import="org.labkey.api.specimen.location.LocationImpl" %> +<%@ page import="org.labkey.api.specimen.location.LocationManager"%> <%@ page import="org.labkey.api.specimen.settings.SettingsManager" %> <%@ page import="org.labkey.api.study.Location" %> <%@ page import="org.labkey.api.study.SpecimenService" %> @@ -36,6 +35,7 @@ <%@ page import="org.labkey.api.view.template.ClientDependencies" %> <%@ page import="org.labkey.specimen.SpecimenManager" %> <%@ page import="org.labkey.specimen.SpecimenRequestManager" %> +<%@ page import="org.labkey.specimen.SpecimenRequestStatus" %> <%@ page import="org.labkey.specimen.actions.ManageRequestBean" %> <%@ page import="org.labkey.specimen.actions.ShowSearchAction" %> <%@ page import="org.labkey.specimen.actions.SpecimenController" %> diff --git a/specimen/src/org/labkey/specimen/view/manageRequestStatus.jsp b/specimen/src/org/labkey/specimen/view/manageRequestStatus.jsp index 9c487201..84f770bf 100644 --- a/specimen/src/org/labkey/specimen/view/manageRequestStatus.jsp +++ b/specimen/src/org/labkey/specimen/view/manageRequestStatus.jsp @@ -15,11 +15,11 @@ * limitations under the License. */ %> -<%@ page import="org.labkey.api.specimen.SpecimenRequestStatus" %> <%@ page import="org.labkey.api.study.SpecimenService" %> -<%@ page import="org.labkey.api.view.HttpView"%> +<%@ page import="org.labkey.api.view.HttpView" %> <%@ page import="org.labkey.api.view.JspView"%> <%@ page import="org.labkey.specimen.SpecimenRequestManager"%> +<%@ page import="org.labkey.specimen.SpecimenRequestStatus"%> <%@ page import="org.labkey.specimen.actions.ManageRequestBean" %> <%@ page import="org.labkey.specimen.actions.SpecimenController" %> <%@ page import="org.labkey.specimen.notifications.ActorNotificationRecipientSet" %> diff --git a/specimen/src/org/labkey/specimen/view/manageStatusOrder.jsp b/specimen/src/org/labkey/specimen/view/manageStatusOrder.jsp index 7b70e9d6..0fc93ccc 100644 --- a/specimen/src/org/labkey/specimen/view/manageStatusOrder.jsp +++ b/specimen/src/org/labkey/specimen/view/manageStatusOrder.jsp @@ -15,11 +15,11 @@ * limitations under the License. */ %> -<%@ page import="org.labkey.api.specimen.SpecimenRequestStatus"%> <%@ page import="org.labkey.api.study.Study"%> <%@ page import="org.labkey.api.view.HttpView"%> <%@ page import="org.labkey.api.view.JspView"%> <%@ page import="org.labkey.specimen.SpecimenRequestManager"%> +<%@ page import="org.labkey.specimen.SpecimenRequestStatus"%> <%@ page import="org.labkey.specimen.actions.SpecimenController.ManageStatusOrderAction" %> <%@ page import="org.labkey.specimen.actions.SpecimenController.ManageStatusesAction" %> <%@ page import="java.util.List" %> diff --git a/specimen/src/org/labkey/specimen/view/manageStatuses.jsp b/specimen/src/org/labkey/specimen/view/manageStatuses.jsp index 7b684478..048a79af 100644 --- a/specimen/src/org/labkey/specimen/view/manageStatuses.jsp +++ b/specimen/src/org/labkey/specimen/view/manageStatuses.jsp @@ -15,15 +15,15 @@ * limitations under the License. */ %> -<%@ page import="org.labkey.api.specimen.SpecimenRequestStatus"%> <%@ page import="org.labkey.api.specimen.settings.SettingsManager"%> <%@ page import="org.labkey.api.specimen.settings.StatusSettings"%> <%@ page import="org.labkey.api.study.Study"%> <%@ page import="org.labkey.api.study.StudyUrls"%> -<%@ page import="org.labkey.api.view.ActionURL" %> +<%@ 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.specimen.SpecimenRequestManager" %> +<%@ page import="org.labkey.specimen.SpecimenRequestStatus" %> <%@ page import="org.labkey.specimen.actions.SpecimenController.DeleteStatusAction" %> <%@ page import="org.labkey.specimen.actions.SpecimenController.ManageStatusOrderAction" %> <%@ page import="org.labkey.specimen.actions.SpecimenController.ManageStatusesAction" %> diff --git a/specimen/src/org/labkey/specimen/view/viewRequestsHeader.jsp b/specimen/src/org/labkey/specimen/view/viewRequestsHeader.jsp index d5ae527b..fbfcada4 100644 --- a/specimen/src/org/labkey/specimen/view/viewRequestsHeader.jsp +++ b/specimen/src/org/labkey/specimen/view/viewRequestsHeader.jsp @@ -15,11 +15,11 @@ * limitations under the License. */ %> -<%@ page import="org.labkey.api.specimen.SpecimenRequestStatus" %> <%@ page import="org.labkey.api.view.ActionURL" %> <%@ page import="org.labkey.api.view.HttpView" %> <%@ page import="org.labkey.api.view.JspView" %> <%@ page import="org.labkey.api.view.ViewContext" %> +<%@ page import="org.labkey.specimen.SpecimenRequestStatus" %> <%@ page import="org.labkey.specimen.actions.ViewRequestsHeaderBean" %> <%@ page extends="org.labkey.api.jsp.JspBase" %> <% diff --git a/specimen/src/org/labkey/specimen/writer/LocationSpecimenWriter.java b/specimen/src/org/labkey/specimen/writer/LocationSpecimenWriter.java index a49821d0..c9fc5dfd 100644 --- a/specimen/src/org/labkey/specimen/writer/LocationSpecimenWriter.java +++ b/specimen/src/org/labkey/specimen/writer/LocationSpecimenWriter.java @@ -18,7 +18,7 @@ import org.labkey.api.data.SQLFragment; import org.labkey.api.data.TableInfo; import org.labkey.api.data.dialect.SqlDialect; -import org.labkey.api.specimen.importer.ImportableColumn; +import org.labkey.specimen.importer.ImportableColumn; import org.labkey.api.study.writer.SimpleStudyExportContext; import java.util.Collection; diff --git a/specimen/src/org/labkey/specimen/writer/SpecimenArchiveWriter.java b/specimen/src/org/labkey/specimen/writer/SpecimenArchiveWriter.java index dc844fd4..b6775986 100644 --- a/specimen/src/org/labkey/specimen/writer/SpecimenArchiveWriter.java +++ b/specimen/src/org/labkey/specimen/writer/SpecimenArchiveWriter.java @@ -27,7 +27,7 @@ import org.labkey.api.data.TableInfoWriter; import org.labkey.api.exp.property.Domain; import org.labkey.api.exp.property.DomainProperty; -import org.labkey.api.specimen.SpecimenColumns; +import org.labkey.specimen.SpecimenColumns; import org.labkey.api.specimen.SpecimenSchema; import org.labkey.api.specimen.model.SpecimenTablesProvider; import org.labkey.api.study.Study; diff --git a/specimen/src/org/labkey/specimen/writer/SpecimenSettingsWriter.java b/specimen/src/org/labkey/specimen/writer/SpecimenSettingsWriter.java index 8e9707f7..ea6a07c5 100644 --- a/specimen/src/org/labkey/specimen/writer/SpecimenSettingsWriter.java +++ b/specimen/src/org/labkey/specimen/writer/SpecimenSettingsWriter.java @@ -17,8 +17,6 @@ import org.jetbrains.annotations.Nullable; import org.labkey.api.security.GroupManager; -import org.labkey.api.specimen.SpecimenRequestStatus; -import org.labkey.api.specimen.importer.RequestabilityManager; import org.labkey.api.specimen.settings.DisplaySettings; import org.labkey.api.specimen.settings.RepositorySettings; import org.labkey.api.specimen.settings.SettingsManager; @@ -31,7 +29,9 @@ import org.labkey.security.xml.GroupsType; import org.labkey.specimen.SpecimenRequestManager; import org.labkey.specimen.SpecimenRequestManager.SpecimenRequestInput; +import org.labkey.specimen.SpecimenRequestStatus; import org.labkey.specimen.actions.ManageReqsBean; +import org.labkey.specimen.importer.RequestabilityManager; import org.labkey.specimen.model.SpecimenRequestActor; import org.labkey.specimen.requirements.SpecimenRequestRequirement; import org.labkey.specimen.requirements.SpecimenRequestRequirementProvider; diff --git a/specimen/src/org/labkey/specimen/writer/SpecimenWriter.java b/specimen/src/org/labkey/specimen/writer/SpecimenWriter.java index b797c154..9265e940 100644 --- a/specimen/src/org/labkey/specimen/writer/SpecimenWriter.java +++ b/specimen/src/org/labkey/specimen/writer/SpecimenWriter.java @@ -34,9 +34,9 @@ import org.labkey.api.exp.PropertyDescriptor; import org.labkey.api.query.UserSchema; import org.labkey.api.specimen.SpecimenSchema; -import org.labkey.api.specimen.SpecimenTableManager; -import org.labkey.api.specimen.importer.SpecimenColumn; -import org.labkey.api.specimen.importer.TargetTable; +import org.labkey.specimen.SpecimenTableManager; +import org.labkey.specimen.importer.SpecimenColumn; +import org.labkey.specimen.importer.TargetTable; import org.labkey.api.study.Study; import org.labkey.api.study.StudyService; import org.labkey.api.study.writer.SimpleStudyExportContext; diff --git a/specimen/src/org/labkey/specimen/writer/StandardSpecimenWriter.java b/specimen/src/org/labkey/specimen/writer/StandardSpecimenWriter.java index 840ba294..f2b8b0b5 100644 --- a/specimen/src/org/labkey/specimen/writer/StandardSpecimenWriter.java +++ b/specimen/src/org/labkey/specimen/writer/StandardSpecimenWriter.java @@ -25,7 +25,7 @@ import org.labkey.api.data.TSVGridWriter; import org.labkey.api.data.TableInfo; import org.labkey.api.specimen.SpecimenSchema; -import org.labkey.api.specimen.importer.ImportableColumn; +import org.labkey.specimen.importer.ImportableColumn; import org.labkey.api.study.writer.SimpleStudyExportContext; import org.labkey.api.writer.VirtualFile; import org.labkey.api.writer.Writer; From 2f706b4cd148adb6b48730448759766097ac65d2 Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Thu, 23 May 2024 17:15:47 -0700 Subject: [PATCH 417/494] Move more specimen code from study into specimen Original PR: https://github.com/LabKey/testAutomation/pull/1945 --- src/org/labkey/test/tests/SpecimenTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/org/labkey/test/tests/SpecimenTest.java b/src/org/labkey/test/tests/SpecimenTest.java index 8443da0f..b7b186e7 100644 --- a/src/org/labkey/test/tests/SpecimenTest.java +++ b/src/org/labkey/test/tests/SpecimenTest.java @@ -945,7 +945,7 @@ private void exportSpecimenTest() { popLocation(); addUrlParameter("&exportType=excelWebQuery"); - assertTextPresent("org.labkey.api.specimen.query.SpecimenRequestDisplayColumn"); + assertTextPresent("org.labkey.specimen.query.SpecimenRequestDisplayColumn"); goBack(); goToAdminConsole().clickAuditLog(); From 1c528c4d878d34d7996b89ff2b9c709482c176ee Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Tue, 28 May 2024 10:33:23 -0700 Subject: [PATCH 418/494] Eliminate DbCache Original PR: https://github.com/LabKey/platform/pull/5519 --- .../src/org/labkey/specimen/SpecimenModule.java | 8 -------- .../labkey/specimen/SpecimenRequestManager.java | 2 -- .../org/labkey/specimen/SpecimenServiceImpl.java | 4 ---- .../specimen/actions/SpecimenController.java | 2 +- .../importer/SpecimenSettingsImporter.java | 6 +----- .../labkey/specimen/settings/SettingsManager.java | 14 +++++--------- .../specimen/view/SpecimenToolsWebPartFactory.java | 2 +- specimen/src/org/labkey/specimen/view/webPart.jsp | 2 +- .../specimen/writer/SpecimenSettingsWriter.java | 8 ++------ 9 files changed, 11 insertions(+), 37 deletions(-) diff --git a/specimen/src/org/labkey/specimen/SpecimenModule.java b/specimen/src/org/labkey/specimen/SpecimenModule.java index e74bb9f6..13b7115a 100644 --- a/specimen/src/org/labkey/specimen/SpecimenModule.java +++ b/specimen/src/org/labkey/specimen/SpecimenModule.java @@ -95,7 +95,6 @@ import java.io.IOException; import java.nio.file.Path; import java.util.Collection; -import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; @@ -317,13 +316,6 @@ protected void startupAfterSpringConfig(ModuleContext moduleContext) } } - @Override - @NotNull - public Collection getSummary(Container c) - { - return Collections.emptyList(); - } - @Override @NotNull public Set getUnitTests() diff --git a/specimen/src/org/labkey/specimen/SpecimenRequestManager.java b/specimen/src/org/labkey/specimen/SpecimenRequestManager.java index 000f2d1b..27272aad 100644 --- a/specimen/src/org/labkey/specimen/SpecimenRequestManager.java +++ b/specimen/src/org/labkey/specimen/SpecimenRequestManager.java @@ -10,7 +10,6 @@ import org.labkey.api.cache.BlockingCache; import org.labkey.api.cache.CacheLoader; import org.labkey.api.cache.CacheManager; -import org.labkey.api.cache.DbCache; import org.labkey.api.data.ColumnInfo; import org.labkey.api.data.Container; import org.labkey.api.data.DatabaseCache; @@ -112,7 +111,6 @@ public List getRequestStatuses(Container c, User user) try (var ignore = SpringActionController.ignoreSqlUpdates()) { Table.insert(user, _requestStatusHelper.getTableInfo(), notYetSubmittedStatus); - DbCache.trackRemove(_requestStatusHelper.getTableInfo()); _requestStatusHelper.clearCache(c); } statuses = _requestStatusHelper.getList(c, "SortOrder"); diff --git a/specimen/src/org/labkey/specimen/SpecimenServiceImpl.java b/specimen/src/org/labkey/specimen/SpecimenServiceImpl.java index 28eb1180..13d841c6 100644 --- a/specimen/src/org/labkey/specimen/SpecimenServiceImpl.java +++ b/specimen/src/org/labkey/specimen/SpecimenServiceImpl.java @@ -20,7 +20,6 @@ import org.apache.logging.log4j.Logger; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.labkey.api.cache.DbCache; import org.labkey.api.data.Container; import org.labkey.api.data.DbSchema; import org.labkey.api.data.PropertyManager; @@ -478,15 +477,12 @@ public void deleteAllSpecimenData(Container c, Set set, User user) Table.delete(SpecimenSchema.get().getTableInfoSampleRequestSpecimen(), containerFilter); assert set.add(SpecimenSchema.get().getTableInfoSampleRequestSpecimen()); Table.delete(SpecimenSchema.get().getTableInfoSampleRequestEvent(), containerFilter); - DbCache.trackRemove(SpecimenSchema.get().getTableInfoSampleRequestEvent()); SpecimenRequestManager.get().clearRequestEventHelper(c); assert set.add(SpecimenSchema.get().getTableInfoSampleRequestEvent()); Table.delete(SpecimenSchema.get().getTableInfoSampleRequest(), containerFilter); - DbCache.trackRemove(SpecimenSchema.get().getTableInfoSampleRequest()); SpecimenRequestManager.get().clearRequestHelper(c); assert set.add(SpecimenSchema.get().getTableInfoSampleRequest()); Table.delete(SpecimenSchema.get().getTableInfoSampleRequestStatus(), containerFilter); - DbCache.trackRemove(SpecimenSchema.get().getTableInfoSampleRequestStatus()); SpecimenRequestManager.get().clearRequestStatusHelper(c); assert set.add(SpecimenSchema.get().getTableInfoSampleRequestStatus()); diff --git a/specimen/src/org/labkey/specimen/actions/SpecimenController.java b/specimen/src/org/labkey/specimen/actions/SpecimenController.java index 75da7aa3..54f4d4e9 100644 --- a/specimen/src/org/labkey/specimen/actions/SpecimenController.java +++ b/specimen/src/org/labkey/specimen/actions/SpecimenController.java @@ -357,7 +357,7 @@ private ActionURL getManageStudyURL() public void ensureSpecimenRequestsConfigured(boolean checkExistingStatuses) { - if (!org.labkey.specimen.settings.SettingsManager.get().isSpecimenRequestEnabled(getContainer(), checkExistingStatuses)) + if (!org.labkey.specimen.settings.SettingsManager.get().isSpecimenRequestEnabled(getContainer(), checkExistingStatuses, getUser())) throw new RedirectException(new ActionURL(SpecimenRequestConfigRequiredAction.class, getContainer())); } diff --git a/specimen/src/org/labkey/specimen/importer/SpecimenSettingsImporter.java b/specimen/src/org/labkey/specimen/importer/SpecimenSettingsImporter.java index c568389b..998c0fbd 100644 --- a/specimen/src/org/labkey/specimen/importer/SpecimenSettingsImporter.java +++ b/specimen/src/org/labkey/specimen/importer/SpecimenSettingsImporter.java @@ -61,16 +61,12 @@ import java.util.Map; import java.util.Set; -/** - * User: kevink - * Date: 6/13/13 - */ public class SpecimenSettingsImporter implements SimpleStudyImporter { @Override public Timing getTiming() { - return Timing.Late; // Can't setup specimen request actors until the locations have been created + return Timing.Late; // Can't set up specimen request actors until the locations have been created } @Override diff --git a/specimen/src/org/labkey/specimen/settings/SettingsManager.java b/specimen/src/org/labkey/specimen/settings/SettingsManager.java index f6bfdc43..beb6c64f 100644 --- a/specimen/src/org/labkey/specimen/settings/SettingsManager.java +++ b/specimen/src/org/labkey/specimen/settings/SettingsManager.java @@ -3,12 +3,11 @@ import org.labkey.api.action.SpringActionController; import org.labkey.api.data.Container; import org.labkey.api.data.PropertyManager; +import org.labkey.api.security.User; import org.labkey.api.security.UserManager; -import org.labkey.api.specimen.SpecimenSchema; import org.labkey.api.specimen.settings.DisplaySettings; import org.labkey.api.specimen.settings.RepositorySettings; import org.labkey.api.specimen.settings.StatusSettings; -import org.labkey.api.study.QueryHelper; import org.labkey.specimen.SpecimenRequestManager; import org.labkey.specimen.SpecimenRequestStatus; @@ -17,8 +16,6 @@ public class SettingsManager { - private final QueryHelper _requestStatusHelper; - private static final SettingsManager INSTANCE = new SettingsManager(); public static SettingsManager get() @@ -28,7 +25,6 @@ public static SettingsManager get() private SettingsManager() { - _requestStatusHelper = new QueryHelper<>(()-> SpecimenSchema.get().getTableInfoSampleRequestStatus(), SpecimenRequestStatus.class); } public RequestNotificationSettings getRequestNotificationSettings(Container container) @@ -81,12 +77,12 @@ public void saveRepositorySettings(Container container, RepositorySettings setti SpecimenRequestManager.get().clearGroupedValuesForColumn(container); // May have changed groupings } - public boolean isSpecimenRequestEnabled(Container container) + public boolean isSpecimenRequestEnabled(Container container, User user) { - return isSpecimenRequestEnabled(container, true); + return isSpecimenRequestEnabled(container, true, user); } - public boolean isSpecimenRequestEnabled(Container container, boolean checkExistingStatuses) + public boolean isSpecimenRequestEnabled(Container container, boolean checkExistingStatuses, User user) { if (!checkExistingStatuses) { @@ -96,7 +92,7 @@ public boolean isSpecimenRequestEnabled(Container container, boolean checkExisti { if (!org.labkey.api.specimen.settings.SettingsManager.get().getRepositorySettings(container).isEnableRequests()) return false; - List statuses = _requestStatusHelper.getList(container, "SortOrder"); + List statuses = SpecimenRequestManager.get().getRequestStatuses(container, user); return (statuses != null && statuses.size() > 1); } } diff --git a/specimen/src/org/labkey/specimen/view/SpecimenToolsWebPartFactory.java b/specimen/src/org/labkey/specimen/view/SpecimenToolsWebPartFactory.java index a49fedcf..84c655f6 100644 --- a/specimen/src/org/labkey/specimen/view/SpecimenToolsWebPartFactory.java +++ b/specimen/src/org/labkey/specimen/view/SpecimenToolsWebPartFactory.java @@ -37,7 +37,7 @@ protected List getItems(ViewContext portalCtx) ActionURL vialSearchURL = ShowSearchAction.getShowSearchURL(portalCtx.getContainer(), true); items.add(new StudyToolsWebPart.Item("Vial Search", iconBase + "specimen_search.png", vialSearchURL)); - if (SettingsManager.get().isSpecimenRequestEnabled(portalCtx.getContainer())) + if (SettingsManager.get().isSpecimenRequestEnabled(portalCtx.getContainer(), portalCtx.getUser())) { if (portalCtx.getContainer().hasPermission(portalCtx.getUser(), RequestSpecimensPermission.class)) items.add(new StudyToolsWebPart.Item("New Request", iconBase + "specimen_request.png", new ActionURL(ShowCreateSpecimenRequestAction.class, portalCtx.getContainer()))); diff --git a/specimen/src/org/labkey/specimen/view/webPart.jsp b/specimen/src/org/labkey/specimen/view/webPart.jsp index 625b8ede..de4c00bb 100644 --- a/specimen/src/org/labkey/specimen/view/webPart.jsp +++ b/specimen/src/org/labkey/specimen/view/webPart.jsp @@ -228,7 +228,7 @@ <% - if (SettingsManager.get().isSpecimenRequestEnabled(c)) + if (SettingsManager.get().isSpecimenRequestEnabled(c, getUser())) { %> diff --git a/specimen/src/org/labkey/specimen/writer/SpecimenSettingsWriter.java b/specimen/src/org/labkey/specimen/writer/SpecimenSettingsWriter.java index ea6a07c5..998981da 100644 --- a/specimen/src/org/labkey/specimen/writer/SpecimenSettingsWriter.java +++ b/specimen/src/org/labkey/specimen/writer/SpecimenSettingsWriter.java @@ -49,10 +49,6 @@ import java.util.Collections; import java.util.List; -/** - * User: kevink - * Date: 6/13/13 - */ public class SpecimenSettingsWriter extends AbstractSpecimenWriter { private static final String DEFAULT_SETTINGS_FILE = "specimen_settings.xml"; @@ -119,7 +115,7 @@ private void writeLocationTypes(SpecimenSettingsType specimenSettingsType, Study private void writeSpecimenGroupings(SpecimenSettingsType specimenSettingsType, RepositorySettings repositorySettings) { ArrayList groupings = repositorySettings.getSpecimenWebPartGroupings(); - if (groupings.size() > 0) + if (!groupings.isEmpty()) { SpecimenSettingsType.WebPartGroupings xmlWebPartGroupings = specimenSettingsType.addNewWebPartGroupings(); for (String[] grouping : groupings) @@ -134,7 +130,7 @@ private void writeRequestStatuses(SpecimenSettingsType specimenSettingsType, Stu { SpecimenSettingsType.RequestStatuses xmlRequestStatuses = null; List statuses = SpecimenRequestManager.get().getRequestStatuses(study.getContainer(), ctx.getUser()); - if (statuses.size() > 0) + if (!statuses.isEmpty()) { for (SpecimenRequestStatus status : statuses) { From 557b2dada142f11b7e887ebcf87dc4079f282dfd Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Tue, 4 Jun 2024 13:57:31 -0700 Subject: [PATCH 419/494] Move protein services to Protein module Original PR: https://github.com/LabKey/platform/pull/5558 --- .../specimen/actions/UpdateSpecimenCommentsBean.java | 7 ------- 1 file changed, 7 deletions(-) diff --git a/specimen/src/org/labkey/specimen/actions/UpdateSpecimenCommentsBean.java b/specimen/src/org/labkey/specimen/actions/UpdateSpecimenCommentsBean.java index b01e9fcf..46ba8d4d 100644 --- a/specimen/src/org/labkey/specimen/actions/UpdateSpecimenCommentsBean.java +++ b/specimen/src/org/labkey/specimen/actions/UpdateSpecimenCommentsBean.java @@ -1,19 +1,12 @@ package org.labkey.specimen.actions; import org.apache.commons.lang3.time.DateUtils; -import org.jetbrains.annotations.NotNull; -import org.labkey.api.annotations.Migrate; -import org.labkey.api.data.Container; import org.labkey.api.specimen.Vial; import org.labkey.api.specimen.model.SpecimenComment; import org.labkey.api.study.Study; import org.labkey.api.study.StudyInternalService; -import org.labkey.api.study.StudyService; -import org.labkey.api.study.StudyUrls; import org.labkey.api.study.TimepointType; import org.labkey.api.study.Visit; -import org.labkey.api.util.PageFlowUtil; -import org.labkey.api.view.RedirectException; import org.labkey.api.view.ViewContext; import org.labkey.specimen.SpecimenManager; From 290554edc3f04c2ef14d678c7d7f7f40f1b91172 Mon Sep 17 00:00:00 2001 From: labkey-matthewb Date: Thu, 27 Jun 2024 12:41:35 -0700 Subject: [PATCH 420/494] class IteratorOfMapsDataIterator Original PR: https://github.com/LabKey/platform/pull/5625 * class IteratorOfMapsDataIterator DataIteratorUtil.mapTransformer() * comment * Update api/src/org/labkey/api/dataiterator/AbstractMapDataIterator.java Co-authored-by: Josh Eckels * remove redundant method call --------- Co-authored-by: Josh Eckels --- .../labkey/specimen/pipeline/SampleMindedTransformTask.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/specimen/src/org/labkey/specimen/pipeline/SampleMindedTransformTask.java b/specimen/src/org/labkey/specimen/pipeline/SampleMindedTransformTask.java index b125c344..cf5e5393 100644 --- a/specimen/src/org/labkey/specimen/pipeline/SampleMindedTransformTask.java +++ b/specimen/src/org/labkey/specimen/pipeline/SampleMindedTransformTask.java @@ -36,11 +36,11 @@ import org.labkey.api.data.SqlExecutor; import org.labkey.api.data.SqlSelector; import org.labkey.api.data.TableInfo; +import org.labkey.api.dataiterator.AbstractMapDataIterator; import org.labkey.api.dataiterator.DataIterator; import org.labkey.api.dataiterator.DataIteratorBuilder; import org.labkey.api.dataiterator.DataIteratorContext; import org.labkey.api.dataiterator.DataIteratorUtil; -import org.labkey.api.dataiterator.ListofMapsDataIterator; import org.labkey.api.dataiterator.LoggingDataIterator; import org.labkey.api.dataiterator.MapDataIterator; import org.labkey.api.pipeline.AbstractSpecimenTransformTask; @@ -597,7 +597,7 @@ public DataIterator getDataIterator(DataIteratorContext context) } } - static class _DataIterator extends ListofMapsDataIterator + static class _DataIterator extends AbstractMapDataIterator.ListOfMapsDataIterator { static DataIterator create(User user, DataIterator source, DataIteratorContext context, Study study, TableInfo target) throws BatchValidationException { @@ -607,7 +607,7 @@ static DataIterator create(User user, DataIterator source, DataIteratorContext c _DataIterator(User user, List cols, DataIterator source, DataIteratorContext context, Study study) throws BatchValidationException { - super(cols); + super(context, cols); try { From e97102a8eb9bd6d732a6ac082a89a7bf7e6fe9e6 Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Tue, 9 Jul 2024 18:35:35 -0700 Subject: [PATCH 421/494] Clean up StudyCache and QueryHelper Original PR: https://github.com/LabKey/platform/pull/5633 --- .../specimen/SpecimenRequestManager.java | 42 +++++++++---------- .../specimen/SpecimenRequestStatus.java | 4 +- .../actions/SpecimenApiController.java | 2 +- .../specimen/actions/SpecimenController.java | 6 +-- .../actions/ViewRequestsHeaderBean.java | 4 +- .../labkey/specimen/model/AdditiveType.java | 4 +- .../labkey/specimen/model/DerivativeType.java | 10 +---- .../specimen/model/SpecimenRequestActor.java | 9 +--- .../specimen/model/SpecimenRequestEvent.java | 4 +- .../report/SpecimenReportExcelWriter.java | 7 ++-- .../report/SpecimenTypeVisitReport.java | 7 +--- .../specimen/report/SpecimenVisitReport.java | 8 ++-- .../request/RequestParticipantReport.java | 9 +--- .../RequestParticipantReportFactory.java | 6 +-- .../TypeParticipantReportFactory.java | 10 ++--- .../requirements/SpecimenRequest.java | 4 +- .../SpecimenRequestRequirement.java | 9 +--- .../specimen/settings/SettingsManager.java | 4 +- .../labkey/specimen/view/autoReportList.jsp | 3 +- .../specimen/view/manageRequestStatus.jsp | 3 +- .../specimen/view/manageStatusOrder.jsp | 4 +- .../labkey/specimen/view/manageStatuses.jsp | 4 +- .../specimen/view/specimenVisitReport.jsp | 4 +- .../writer/SpecimenSettingsWriter.java | 3 +- 24 files changed, 72 insertions(+), 98 deletions(-) diff --git a/specimen/src/org/labkey/specimen/SpecimenRequestManager.java b/specimen/src/org/labkey/specimen/SpecimenRequestManager.java index 27272aad..355218ac 100644 --- a/specimen/src/org/labkey/specimen/SpecimenRequestManager.java +++ b/specimen/src/org/labkey/specimen/SpecimenRequestManager.java @@ -41,6 +41,7 @@ import org.labkey.api.specimen.security.permissions.RequestSpecimensPermission; import org.labkey.api.specimen.settings.SettingsManager; import org.labkey.api.study.QueryHelper; +import org.labkey.api.study.QueryHelper.StudyCacheCollections; import org.labkey.api.study.SpecimenUrls; import org.labkey.api.study.Study; import org.labkey.api.study.StudyService; @@ -77,9 +78,9 @@ public class SpecimenRequestManager { private static final SpecimenRequestManager INSTANCE = new SpecimenRequestManager(); - private final QueryHelper _requestEventHelper; - private final QueryHelper _requestStatusHelper; - private final QueryHelper _requestHelper; + private final QueryHelper> _requestEventHelper; + private final QueryHelper> _requestStatusHelper; + private final QueryHelper> _requestHelper; public static SpecimenRequestManager get() { @@ -89,18 +90,18 @@ public static SpecimenRequestManager get() private SpecimenRequestManager() { _requestEventHelper = new QueryHelper<>(() -> SpecimenSchema.get().getTableInfoSampleRequestEvent(), SpecimenRequestEvent.class); - _requestStatusHelper = new QueryHelper<>(() -> SpecimenSchema.get().getTableInfoSampleRequestStatus(), SpecimenRequestStatus.class); - _requestHelper = new QueryHelper<>(() -> SpecimenSchema.get().getTableInfoSampleRequest(), SpecimenRequest.class); + _requestStatusHelper = new QueryHelper<>(() -> SpecimenSchema.get().getTableInfoSampleRequestStatus(), SpecimenRequestStatus.class, "SortOrder"); + _requestHelper = new QueryHelper<>(() -> SpecimenSchema.get().getTableInfoSampleRequest(), SpecimenRequest.class, "-Created"); initGroupedValueAllowedColumnMap(); } public List getRequestStatuses(Container c, User user) { - List statuses = _requestStatusHelper.getList(c, "SortOrder"); + List statuses = new ArrayList<>(_requestStatusHelper.getCollection(c)); // if the 'not-yet-submitted' status doesn't exist, create it here, with sort order -1, // so it's always first. - if (statuses == null || statuses.isEmpty() || statuses.get(0).getSortOrder() != -1) + if (statuses.isEmpty() || statuses.get(0).getSortOrder() != -1) { SpecimenRequestStatus notYetSubmittedStatus = new SpecimenRequestStatus(); notYetSubmittedStatus.setContainer(c); @@ -113,7 +114,7 @@ public List getRequestStatuses(Container c, User user) Table.insert(user, _requestStatusHelper.getTableInfo(), notYetSubmittedStatus); _requestStatusHelper.clearCache(c); } - statuses = _requestStatusHelper.getList(c, "SortOrder"); + statuses = new ArrayList<>(_requestStatusHelper.getCollection(c)); } return statuses; } @@ -156,7 +157,7 @@ public boolean hasEditRequestPermissions(User user, SpecimenRequest request) public Set getRequestStatusIdsInUse(Container c) { - List requests = _requestHelper.getList(c); + Collection requests = _requestHelper.getCollection(c); Set uniqueStatuses = new HashSet<>(); for (SpecimenRequest request : requests) uniqueStatuses.add(request.getStatusId()); @@ -528,12 +529,12 @@ private SpecimenRequestEvent createRequestEvent(User user, Container container, return event; } - public List getRequests(Container c, User user) + public Collection getRequests(Container c, @Nullable User user) { - SimpleFilter filter = new SimpleFilter(FieldKey.fromParts("Hidden"), Boolean.FALSE); - if (user != null) - filter.addCondition(FieldKey.fromParts("CreatedBy"), user.getUserId()); - return _requestHelper.getList(c, filter, "-Created"); + return _requestHelper.getCollection(c).stream() + .filter(sr -> !sr.isHidden()) + .filter(sr -> user == null || sr.getCreatedBy() == user.getUserId()) + .toList(); } public SpecimenRequest getRequest(Container c, int rowId) @@ -1217,13 +1218,12 @@ public List getMissingSpecimens(SpecimenRequest specimenRequest) private void deleteRequestEvents(SpecimenRequest request) { - SimpleFilter filter = new SimpleFilter(FieldKey.fromParts("RequestId"), request.getRowId()); - List events = _requestEventHelper.getList(request.getContainer(), filter); - for (SpecimenRequestEvent event : events) - { - AttachmentService.get().deleteAttachments(event); - _requestEventHelper.delete(event); - } + _requestEventHelper.getCollection(request.getContainer()).stream() + .filter(event -> event.getRequestId() == request.getRowId()) + .forEach(event -> { + AttachmentService.get().deleteAttachments(event); + _requestEventHelper.delete(event); + }); } public RequestedSpecimens getRequestableBySpecimenHash(Container c, User user, Set formValues, Integer preferredLocation) throws AmbiguousLocationException diff --git a/specimen/src/org/labkey/specimen/SpecimenRequestStatus.java b/specimen/src/org/labkey/specimen/SpecimenRequestStatus.java index 76348c49..b67145db 100644 --- a/specimen/src/org/labkey/specimen/SpecimenRequestStatus.java +++ b/specimen/src/org/labkey/specimen/SpecimenRequestStatus.java @@ -21,7 +21,7 @@ import java.util.Objects; -public class SpecimenRequestStatus extends AbstractStudyCachable +public class SpecimenRequestStatus extends AbstractStudyCachable { private int _rowId; private Container _container; @@ -55,7 +55,7 @@ public void setLabel(String label) } @Override - public Object getPrimaryKey() + public Integer getPrimaryKey() { return getRowId(); } diff --git a/specimen/src/org/labkey/specimen/actions/SpecimenApiController.java b/specimen/src/org/labkey/specimen/actions/SpecimenApiController.java index 56877835..f6528284 100644 --- a/specimen/src/org/labkey/specimen/actions/SpecimenApiController.java +++ b/specimen/src/org/labkey/specimen/actions/SpecimenApiController.java @@ -247,7 +247,7 @@ public ApiResponse execute(GetRequestsForm requestsForm, BindException errors) boolean allUsers = getContainer().hasPermission(getUser(), ManageRequestsPermission.class); if (requestsForm.isAllUsers() != null) allUsers = requestsForm.isAllUsers(); - List allUserRequests = SpecimenRequestManager.get().getRequests(container, allUsers ? null : user); + Collection allUserRequests = SpecimenRequestManager.get().getRequests(container, allUsers ? null : user); List nonFinalRequests = new ArrayList<>(); for (SpecimenRequest request : allUserRequests) { diff --git a/specimen/src/org/labkey/specimen/actions/SpecimenController.java b/specimen/src/org/labkey/specimen/actions/SpecimenController.java index 54f4d4e9..44632ab5 100644 --- a/specimen/src/org/labkey/specimen/actions/SpecimenController.java +++ b/specimen/src/org/labkey/specimen/actions/SpecimenController.java @@ -2911,7 +2911,7 @@ private void updateRequestStatusOrder(Container container, int[] rowIds, boolean private Map getIdToRequestStatusMap(Container container) { - List statuses = SpecimenRequestManager.get().getRequestStatuses(container, getUser()); + Collection statuses = SpecimenRequestManager.get().getRequestStatuses(container, getUser()); Map idToStatus = new HashMap<>(); for (SpecimenRequestStatus status : statuses) idToStatus.put(status.getRowId(), status); @@ -3031,7 +3031,7 @@ public boolean handlePost(StatusEditForm form, BindException errors) { SpecimenRequestStatus status = new SpecimenRequestStatus(); status.setLabel(form.getNewLabel()); - List statuses = SpecimenRequestManager.get().getRequestStatuses(getContainer(), getUser()); + Collection statuses = SpecimenRequestManager.get().getRequestStatuses(getContainer(), getUser()); status.setSortOrder(statuses.size()); status.setContainer(getContainer()); status.setFinalState(form.isNewFinalState()); @@ -3101,7 +3101,7 @@ public void validateCommand(IdForm target, Errors errors) @Override public boolean handlePost(IdForm form, BindException errors) throws Exception { - List statuses = SpecimenRequestManager.get().getRequestStatuses(getContainer(), getUser()); + Collection statuses = SpecimenRequestManager.get().getRequestStatuses(getContainer(), getUser()); SpecimenRequestStatus status = SpecimenRequestManager.get().getRequestStatus(getContainer(), form.getId()); if (status != null) { diff --git a/specimen/src/org/labkey/specimen/actions/ViewRequestsHeaderBean.java b/specimen/src/org/labkey/specimen/actions/ViewRequestsHeaderBean.java index df2ee08d..83489d00 100644 --- a/specimen/src/org/labkey/specimen/actions/ViewRequestsHeaderBean.java +++ b/specimen/src/org/labkey/specimen/actions/ViewRequestsHeaderBean.java @@ -5,7 +5,7 @@ import org.labkey.specimen.SpecimenRequestStatus; import org.labkey.specimen.query.SpecimenRequestQueryView; -import java.util.List; +import java.util.Collection; public class ViewRequestsHeaderBean { @@ -26,7 +26,7 @@ public SpecimenRequestQueryView getView() return _view; } - public List getStauses() + public Collection getStauses() { return SpecimenRequestManager.get().getRequestStatuses(_context.getContainer(), _context.getUser()); } diff --git a/specimen/src/org/labkey/specimen/model/AdditiveType.java b/specimen/src/org/labkey/specimen/model/AdditiveType.java index e8b244e5..86f64c09 100644 --- a/specimen/src/org/labkey/specimen/model/AdditiveType.java +++ b/specimen/src/org/labkey/specimen/model/AdditiveType.java @@ -21,7 +21,7 @@ import java.util.Map; -public class AdditiveType extends AbstractStudyCachable +public class AdditiveType extends AbstractStudyCachable { private long _rowId; // serial NOT NULL, private Container _container; // entityid NOT NULL, @@ -42,7 +42,7 @@ public AdditiveType(Container container, Map map) } @Override - public Object getPrimaryKey() + public Long getPrimaryKey() { return _rowId; } diff --git a/specimen/src/org/labkey/specimen/model/DerivativeType.java b/specimen/src/org/labkey/specimen/model/DerivativeType.java index 28f9d2c0..621fb838 100644 --- a/specimen/src/org/labkey/specimen/model/DerivativeType.java +++ b/specimen/src/org/labkey/specimen/model/DerivativeType.java @@ -21,13 +21,7 @@ import java.util.Map; -/* - * User: brittp - * Date: Dec 18, 2008 - * Time: 12:48:03 PM - */ - -public class DerivativeType extends AbstractStudyCachable +public class DerivativeType extends AbstractStudyCachable { private long _rowId; // serial NOT NULL, private Container _container; // entityid NOT NULL, @@ -48,7 +42,7 @@ public DerivativeType(Container container, Map map) } @Override - public Object getPrimaryKey() + public Long getPrimaryKey() { return _rowId; } diff --git a/specimen/src/org/labkey/specimen/model/SpecimenRequestActor.java b/specimen/src/org/labkey/specimen/model/SpecimenRequestActor.java index e8377212..8fcbadd9 100644 --- a/specimen/src/org/labkey/specimen/model/SpecimenRequestActor.java +++ b/specimen/src/org/labkey/specimen/model/SpecimenRequestActor.java @@ -23,12 +23,7 @@ import org.labkey.api.util.MemTracker; import org.labkey.specimen.requirements.DefaultActor; -/** - * User: brittp - * Date: Feb 8, 2006 - * Time: 4:18:20 PM - */ -public class SpecimenRequestActor extends DefaultActor implements StudyCachable, Cloneable +public class SpecimenRequestActor extends DefaultActor implements StudyCachable, Cloneable { private int _rowId; private Integer _sortOrder; @@ -103,7 +98,7 @@ public void setLabel(String label) } @Override - public Object getPrimaryKey() + public Integer getPrimaryKey() { return getRowId(); } diff --git a/specimen/src/org/labkey/specimen/model/SpecimenRequestEvent.java b/specimen/src/org/labkey/specimen/model/SpecimenRequestEvent.java index 1f826ee6..3b0bfea3 100644 --- a/specimen/src/org/labkey/specimen/model/SpecimenRequestEvent.java +++ b/specimen/src/org/labkey/specimen/model/SpecimenRequestEvent.java @@ -25,7 +25,7 @@ import java.util.Date; import java.util.Objects; -public class SpecimenRequestEvent extends AbstractStudyCachable implements AttachmentParent +public class SpecimenRequestEvent extends AbstractStudyCachable implements AttachmentParent { private int _rowId; private String _entityId; @@ -124,7 +124,7 @@ public void setRequestId(int requestId) } @Override - public Object getPrimaryKey() + public Integer getPrimaryKey() { return getRowId(); } diff --git a/specimen/src/org/labkey/specimen/report/SpecimenReportExcelWriter.java b/specimen/src/org/labkey/specimen/report/SpecimenReportExcelWriter.java index bd46b463..a37e0887 100644 --- a/specimen/src/org/labkey/specimen/report/SpecimenReportExcelWriter.java +++ b/specimen/src/org/labkey/specimen/report/SpecimenReportExcelWriter.java @@ -15,6 +15,8 @@ */ package org.labkey.specimen.report; +import jakarta.servlet.ServletOutputStream; +import jakarta.servlet.http.HttpServletResponse; import jxl.Workbook; import jxl.WorkbookSettings; import jxl.write.Label; @@ -29,9 +31,8 @@ import org.labkey.api.study.Visit; import org.labkey.api.util.ExceptionUtil; -import jakarta.servlet.ServletOutputStream; -import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; +import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -104,7 +105,7 @@ private void writeReport(WritableWorkbook workbook, SpecimenVisitReport report) WritableSheet sheet = workbook.createSheet(sheetName, workbook.getNumberOfSheets()); - List visits = report.getVisits(); + List visits = new ArrayList(report.getVisits()); // Merge cells at top of sheet and write the headers // One or more embedded tabs split a header into equally spaced columns diff --git a/specimen/src/org/labkey/specimen/report/SpecimenTypeVisitReport.java b/specimen/src/org/labkey/specimen/report/SpecimenTypeVisitReport.java index 76f305b2..e3b93a14 100644 --- a/specimen/src/org/labkey/specimen/report/SpecimenTypeVisitReport.java +++ b/specimen/src/org/labkey/specimen/report/SpecimenTypeVisitReport.java @@ -26,17 +26,12 @@ import java.util.Collection; import java.util.Iterator; -import java.util.List; import java.util.Map; import java.util.TreeMap; -/** - * User: brittp - * Created: Jan 14, 2008 9:44:25 AM - */ public class SpecimenTypeVisitReport extends SpecimenVisitReport { - public SpecimenTypeVisitReport(String titlePrefix, List visits, SimpleFilter filter, SpecimenVisitReportParameters parameters) + public SpecimenTypeVisitReport(String titlePrefix, Collection visits, SimpleFilter filter, SpecimenVisitReportParameters parameters) { super(titlePrefix, visits, filter, parameters); } diff --git a/specimen/src/org/labkey/specimen/report/SpecimenVisitReport.java b/specimen/src/org/labkey/specimen/report/SpecimenVisitReport.java index 1be3970b..6200ca40 100644 --- a/specimen/src/org/labkey/specimen/report/SpecimenVisitReport.java +++ b/specimen/src/org/labkey/specimen/report/SpecimenVisitReport.java @@ -24,7 +24,6 @@ import org.labkey.api.query.UserSchema; import org.labkey.api.security.User; import org.labkey.api.specimen.SpecimenQuerySchema; -import org.labkey.specimen.query.SpecimenQueryView; import org.labkey.api.study.CohortFilter; import org.labkey.api.study.Study; import org.labkey.api.study.StudyService; @@ -34,6 +33,7 @@ import org.labkey.api.util.PageFlowUtil; import org.labkey.api.view.ActionURL; import org.labkey.api.view.NotFoundException; +import org.labkey.specimen.query.SpecimenQueryView; import java.util.ArrayList; import java.util.Collection; @@ -47,7 +47,7 @@ */ public abstract class SpecimenVisitReport { - private final List _visits; + private final Collection _visits; private final SpecimenVisitReportParameters _parameters; private final boolean _viewParticipantCount; private final boolean _viewVolume; @@ -62,7 +62,7 @@ public abstract class SpecimenVisitReport _rows; private List _nonEmptyVisits; - public SpecimenVisitReport(String titlePrefix, List visits, SimpleFilter filter, SpecimenVisitReportParameters parameters) + public SpecimenVisitReport(String titlePrefix, Collection visits, SimpleFilter filter, SpecimenVisitReportParameters parameters) { _visits = visits; _filter = filter; @@ -101,7 +101,7 @@ protected ActionURL updateURLFilterParameter(ActionURL url, String columnBase, O return url; } - public List getVisits() + public Collection getVisits() { // ensure rows and non-empty columns have been generated getRows(); diff --git a/specimen/src/org/labkey/specimen/report/request/RequestParticipantReport.java b/specimen/src/org/labkey/specimen/report/request/RequestParticipantReport.java index 54758b3d..76a90105 100644 --- a/specimen/src/org/labkey/specimen/report/request/RequestParticipantReport.java +++ b/specimen/src/org/labkey/specimen/report/request/RequestParticipantReport.java @@ -16,29 +16,24 @@ package org.labkey.specimen.report.request; import org.labkey.api.data.SimpleFilter; -import org.labkey.specimen.query.SpecimenQueryView; import org.labkey.api.study.Visit; import org.labkey.api.view.ActionURL; import org.labkey.specimen.actions.SpecimenController; +import org.labkey.specimen.query.SpecimenQueryView; import org.labkey.specimen.report.RequestSummaryByVisitType; import org.labkey.specimen.report.SpecimenReportManager; import org.labkey.specimen.report.SpecimenTypeLevel; import org.labkey.specimen.report.SpecimenVisitReport; import java.util.Collection; -import java.util.List; import java.util.Map; import java.util.TreeMap; -/** - * User: brittp - * Created: Feb 5, 2008 2:17:53 PM - */ public class RequestParticipantReport extends SpecimenVisitReport { private final boolean _completeRequestsOnly; - public RequestParticipantReport(String titlePrefix, List visits, SimpleFilter filter, RequestParticipantReportFactory parameters) + public RequestParticipantReport(String titlePrefix, Collection visits, SimpleFilter filter, RequestParticipantReportFactory parameters) { super(titlePrefix, visits, filter, parameters); _completeRequestsOnly = parameters.isCompletedRequestsOnly(); diff --git a/specimen/src/org/labkey/specimen/report/request/RequestParticipantReportFactory.java b/specimen/src/org/labkey/specimen/report/request/RequestParticipantReportFactory.java index e1169435..82e7b7ea 100644 --- a/specimen/src/org/labkey/specimen/report/request/RequestParticipantReportFactory.java +++ b/specimen/src/org/labkey/specimen/report/request/RequestParticipantReportFactory.java @@ -113,15 +113,15 @@ protected List createReports() } List reports = new ArrayList<>(); - Map> visitListCache = new HashMap<>(); // cohort rowId -> visit + Map> visitListCache = new HashMap<>(); // cohort rowId -> visit boolean showCohorts = StudyService.get().showCohorts(getContainer(), getUser()); - List allVisits = null; + Collection allVisits = null; Study study = StudyService.get().getStudy(getContainer()); for (String participantId : participantIds) { SimpleFilter filter = new SimpleFilter(FieldKey.fromParts(StudyService.get().getSubjectColumnName(getContainer())), participantId); addBaseFilters(filter); - List visits = null; + Collection visits = null; if (showCohorts) { Cohort cohort = CohortService.get().getCurrentCohortForParticipant(getContainer(), getUser(), participantId); diff --git a/specimen/src/org/labkey/specimen/report/specimentype/TypeParticipantReportFactory.java b/specimen/src/org/labkey/specimen/report/specimentype/TypeParticipantReportFactory.java index 4753b229..cbcb00b9 100644 --- a/specimen/src/org/labkey/specimen/report/specimentype/TypeParticipantReportFactory.java +++ b/specimen/src/org/labkey/specimen/report/specimentype/TypeParticipantReportFactory.java @@ -41,10 +41,6 @@ import java.util.List; import java.util.Map; -/** - * User: brittp - * Created: Jan 24, 2008 1:38:15 PM - */ public class TypeParticipantReportFactory extends TypeReportFactory { private String _participantId; @@ -106,15 +102,15 @@ protected List createReports() return Collections.emptyList(); } List reports = new ArrayList<>(); - Map> visitListCache = new HashMap<>(); // cohort rowId -> visits + Map> visitListCache = new HashMap<>(); // cohort rowId -> visits boolean showCohorts = StudyService.get().showCohorts(getContainer(), getUser()); - List allVisits = null; + Collection allVisits = null; Study study = StudyService.get().getStudy(getContainer()); for (String participantId : participantIds) { SimpleFilter filter = new SimpleFilter(FieldKey.fromParts(StudyService.get().getSubjectColumnName(getContainer())), participantId); addBaseFilters(filter); - List visits = null; + Collection visits = null; if (showCohorts) { Cohort cohort = CohortService.get().getCurrentCohortForParticipant(getContainer(), getUser(), participantId); diff --git a/specimen/src/org/labkey/specimen/requirements/SpecimenRequest.java b/specimen/src/org/labkey/specimen/requirements/SpecimenRequest.java index fc6023f0..4d73a45e 100644 --- a/specimen/src/org/labkey/specimen/requirements/SpecimenRequest.java +++ b/specimen/src/org/labkey/specimen/requirements/SpecimenRequest.java @@ -32,7 +32,7 @@ import java.util.List; import java.util.Objects; -public class SpecimenRequest extends AbstractStudyCachable implements RequirementOwner +public class SpecimenRequest extends AbstractStudyCachable implements RequirementOwner { private Container _container; private String _entityId; @@ -47,7 +47,7 @@ public class SpecimenRequest extends AbstractStudyCachable impl private boolean _hidden; @Override - public Object getPrimaryKey() + public Integer getPrimaryKey() { return getRowId(); } diff --git a/specimen/src/org/labkey/specimen/requirements/SpecimenRequestRequirement.java b/specimen/src/org/labkey/specimen/requirements/SpecimenRequestRequirement.java index 7b7d78ca..20f560ab 100644 --- a/specimen/src/org/labkey/specimen/requirements/SpecimenRequestRequirement.java +++ b/specimen/src/org/labkey/specimen/requirements/SpecimenRequestRequirement.java @@ -25,13 +25,8 @@ import org.labkey.api.util.MemTracker; import org.labkey.specimen.model.SpecimenRequestActor; -/** - * User: brittp - * Date: Feb 8, 2006 - * Time: 4:18:29 PM - */ public class SpecimenRequestRequirement extends DefaultRequirement - implements StudyCachable, Cloneable + implements StudyCachable, Cloneable { private int _rowId; private Container _container; @@ -147,7 +142,7 @@ public void setRequestId(Integer requestId) } @Override - public Object getPrimaryKey() + public Integer getPrimaryKey() { return getRowId(); } diff --git a/specimen/src/org/labkey/specimen/settings/SettingsManager.java b/specimen/src/org/labkey/specimen/settings/SettingsManager.java index beb6c64f..fcff7ea8 100644 --- a/specimen/src/org/labkey/specimen/settings/SettingsManager.java +++ b/specimen/src/org/labkey/specimen/settings/SettingsManager.java @@ -11,7 +11,7 @@ import org.labkey.specimen.SpecimenRequestManager; import org.labkey.specimen.SpecimenRequestStatus; -import java.util.List; +import java.util.Collection; import java.util.Map; public class SettingsManager @@ -92,7 +92,7 @@ public boolean isSpecimenRequestEnabled(Container container, boolean checkExisti { if (!org.labkey.api.specimen.settings.SettingsManager.get().getRepositorySettings(container).isEnableRequests()) return false; - List statuses = SpecimenRequestManager.get().getRequestStatuses(container, user); + Collection statuses = SpecimenRequestManager.get().getRequestStatuses(container, user); return (statuses != null && statuses.size() > 1); } } diff --git a/specimen/src/org/labkey/specimen/view/autoReportList.jsp b/specimen/src/org/labkey/specimen/view/autoReportList.jsp index a14b17de..e327b9c7 100644 --- a/specimen/src/org/labkey/specimen/view/autoReportList.jsp +++ b/specimen/src/org/labkey/specimen/view/autoReportList.jsp @@ -35,6 +35,7 @@ <%@ page import="org.labkey.api.view.template.FrameFactoryClassic" %> <%@ page import="org.labkey.specimen.actions.ReportConfigurationBean" %> <%@ page import="org.labkey.specimen.report.SpecimenVisitReportParameters" %> +<%@ page import="java.util.Collection" %> <%@ page import="java.util.List" %> <%@ page import="java.util.Map" %> <%@ taglib prefix="labkey" uri="http://www.labkey.org/taglib" %> @@ -46,7 +47,7 @@ User user = getUser(); boolean showCohorts = StudyService.get().showCohorts(container, user); Study study = StudyService.get().getStudy(container); - List cohorts = null; + Collection cohorts = null; if (showCohorts) cohorts = CohortService.get().getCohorts(container, user); HtmlString optionLabelStyle = HtmlString.unsafe("text-align: left; padding: 5px 5px 0 5px;"); diff --git a/specimen/src/org/labkey/specimen/view/manageRequestStatus.jsp b/specimen/src/org/labkey/specimen/view/manageRequestStatus.jsp index 84f770bf..586b8247 100644 --- a/specimen/src/org/labkey/specimen/view/manageRequestStatus.jsp +++ b/specimen/src/org/labkey/specimen/view/manageRequestStatus.jsp @@ -23,13 +23,14 @@ <%@ page import="org.labkey.specimen.actions.ManageRequestBean" %> <%@ page import="org.labkey.specimen.actions.SpecimenController" %> <%@ page import="org.labkey.specimen.notifications.ActorNotificationRecipientSet" %> +<%@ page import="java.util.Collection" %> <%@ page import="java.util.List" %> <%@ page extends="org.labkey.api.jsp.JspBase" %> <%@ taglib prefix="labkey" uri="http://www.labkey.org/taglib" %> <% JspView me = (JspView) HttpView.currentView(); ManageRequestBean bean = me.getModelBean(); - List statuses = SpecimenRequestManager.get().getRequestStatuses(getContainer(), getUser()); + Collection statuses = SpecimenRequestManager.get().getRequestStatuses(getContainer(), getUser()); %> diff --git a/specimen/src/org/labkey/specimen/view/manageStatusOrder.jsp b/specimen/src/org/labkey/specimen/view/manageStatusOrder.jsp index 0fc93ccc..884140d7 100644 --- a/specimen/src/org/labkey/specimen/view/manageStatusOrder.jsp +++ b/specimen/src/org/labkey/specimen/view/manageStatusOrder.jsp @@ -22,13 +22,13 @@ <%@ page import="org.labkey.specimen.SpecimenRequestStatus"%> <%@ page import="org.labkey.specimen.actions.SpecimenController.ManageStatusOrderAction" %> <%@ page import="org.labkey.specimen.actions.SpecimenController.ManageStatusesAction" %> -<%@ page import="java.util.List" %> +<%@ page import="java.util.Collection" %> <%@ taglib prefix="labkey" uri="http://www.labkey.org/taglib" %> <%@ page extends="org.labkey.api.jsp.JspBase" %> <% JspView me = (JspView) HttpView.currentView(); Study study = me.getModelBean(); - List statuses = SpecimenRequestManager.get().getRequestStatuses(study.getContainer(), getUser()); + Collection statuses = SpecimenRequestManager.get().getRequestStatuses(study.getContainer(), getUser()); %> <% - JspView me = (JspView) HttpView.currentView(); + JspView me = HttpView.currentView(); RequestNotificationSettings bean = me.getModelBean(); Container container = getContainer(); diff --git a/specimen/src/org/labkey/specimen/view/manageRequest.jsp b/specimen/src/org/labkey/specimen/view/manageRequest.jsp index 5e2e32b8..0403819d 100644 --- a/specimen/src/org/labkey/specimen/view/manageRequest.jsp +++ b/specimen/src/org/labkey/specimen/view/manageRequest.jsp @@ -65,7 +65,7 @@ } %> <% - JspView me = (JspView) HttpView.currentView(); + JspView me = HttpView.currentView(); Container c = getContainer(); User user = getUser(); ManageRequestBean bean = me.getModelBean(); diff --git a/specimen/src/org/labkey/specimen/view/manageRequestInputs.jsp b/specimen/src/org/labkey/specimen/view/manageRequestInputs.jsp index 2eb5a47e..8d20c6be 100644 --- a/specimen/src/org/labkey/specimen/view/manageRequestInputs.jsp +++ b/specimen/src/org/labkey/specimen/view/manageRequestInputs.jsp @@ -24,7 +24,7 @@ <%@ taglib prefix="labkey" uri="http://www.labkey.org/taglib" %> <%@ page extends="org.labkey.api.jsp.JspBase" %> <% - JspView me = (JspView) HttpView.currentView(); + JspView me = HttpView.currentView(); ManageRequestInputsBean bean = me.getModelBean(); String tdTitle = ""; diff --git a/specimen/src/org/labkey/specimen/view/manageRequestStatus.jsp b/specimen/src/org/labkey/specimen/view/manageRequestStatus.jsp index 586b8247..42784ebb 100644 --- a/specimen/src/org/labkey/specimen/view/manageRequestStatus.jsp +++ b/specimen/src/org/labkey/specimen/view/manageRequestStatus.jsp @@ -28,7 +28,7 @@ <%@ page extends="org.labkey.api.jsp.JspBase" %> <%@ taglib prefix="labkey" uri="http://www.labkey.org/taglib" %> <% - JspView me = (JspView) HttpView.currentView(); + JspView me = HttpView.currentView(); ManageRequestBean bean = me.getModelBean(); Collection statuses = SpecimenRequestManager.get().getRequestStatuses(getContainer(), getUser()); %> diff --git a/specimen/src/org/labkey/specimen/view/manageRequirement.jsp b/specimen/src/org/labkey/specimen/view/manageRequirement.jsp index 71dc7054..4057fa68 100644 --- a/specimen/src/org/labkey/specimen/view/manageRequirement.jsp +++ b/specimen/src/org/labkey/specimen/view/manageRequirement.jsp @@ -38,7 +38,7 @@ } %> <% - JspView me = (JspView) HttpView.currentView(); + JspView me = HttpView.currentView(); ManageRequirementBean bean = me.getModelBean(); SpecimenRequestRequirement requirement = bean.getRequirement(); Location location = requirement.getLocation(); diff --git a/specimen/src/org/labkey/specimen/view/manageSpecimenWebPart.jsp b/specimen/src/org/labkey/specimen/view/manageSpecimenWebPart.jsp index 5e76f465..c8d5d51a 100644 --- a/specimen/src/org/labkey/specimen/view/manageSpecimenWebPart.jsp +++ b/specimen/src/org/labkey/specimen/view/manageSpecimenWebPart.jsp @@ -15,10 +15,10 @@ * limitations under the License. */ %> -<%@ page import="org.labkey.specimen.actions.SpecimenController.SpecimenWebPartForm" %> <%@ 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.specimen.actions.SpecimenController.SpecimenWebPartForm" %> <%@ page extends="org.labkey.api.jsp.JspBase" %> <%! @Override @@ -28,7 +28,7 @@ } %> <% - JspView me = (JspView) HttpView.currentView(); + JspView me = HttpView.currentView(); SpecimenWebPartForm bean = me.getModelBean(); String[] grouping1 = bean.getGrouping1(); String[] grouping2 = bean.getGrouping2(); diff --git a/specimen/src/org/labkey/specimen/view/manageStatusOrder.jsp b/specimen/src/org/labkey/specimen/view/manageStatusOrder.jsp index 884140d7..b971827f 100644 --- a/specimen/src/org/labkey/specimen/view/manageStatusOrder.jsp +++ b/specimen/src/org/labkey/specimen/view/manageStatusOrder.jsp @@ -26,7 +26,7 @@ <%@ taglib prefix="labkey" uri="http://www.labkey.org/taglib" %> <%@ page extends="org.labkey.api.jsp.JspBase" %> <% - JspView me = (JspView) HttpView.currentView(); + JspView me = HttpView.currentView(); Study study = me.getModelBean(); Collection statuses = SpecimenRequestManager.get().getRequestStatuses(study.getContainer(), getUser()); %> diff --git a/specimen/src/org/labkey/specimen/view/manageStatuses.jsp b/specimen/src/org/labkey/specimen/view/manageStatuses.jsp index 1cb43c36..05d88773 100644 --- a/specimen/src/org/labkey/specimen/view/manageStatuses.jsp +++ b/specimen/src/org/labkey/specimen/view/manageStatuses.jsp @@ -15,24 +15,24 @@ * limitations under the License. */ %> -<%@ page import="org.labkey.specimen.settings.StatusSettings"%> <%@ page import="org.labkey.api.study.Study"%> <%@ 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.JspView"%> <%@ page import="org.labkey.specimen.SpecimenRequestManager" %> <%@ page import="org.labkey.specimen.SpecimenRequestStatus" %> <%@ page import="org.labkey.specimen.actions.SpecimenController.DeleteStatusAction" %> <%@ page import="org.labkey.specimen.actions.SpecimenController.ManageStatusOrderAction" %> <%@ page import="org.labkey.specimen.actions.SpecimenController.ManageStatusesAction" %> <%@ page import="org.labkey.specimen.settings.SettingsManager" %> +<%@ page import="org.labkey.specimen.settings.StatusSettings" %> <%@ page import="java.util.Collection" %> <%@ page import="java.util.Set" %> <%@ taglib prefix="labkey" uri="http://www.labkey.org/taglib" %> <%@ page extends="org.labkey.api.jsp.JspBase" %> <% - JspView me = (JspView) HttpView.currentView(); + JspView me = HttpView.currentView(); Study study = me.getModelBean(); Collection statuses = SpecimenRequestManager.get().getRequestStatuses(study.getContainer(), getUser()); Set inUseStatuses = SpecimenRequestManager.get().getRequestStatusIdsInUse(study.getContainer()); diff --git a/specimen/src/org/labkey/specimen/view/requestSpecimens.jsp b/specimen/src/org/labkey/specimen/view/requestSpecimens.jsp index 87328411..d575a666 100644 --- a/specimen/src/org/labkey/specimen/view/requestSpecimens.jsp +++ b/specimen/src/org/labkey/specimen/view/requestSpecimens.jsp @@ -47,7 +47,7 @@ } %> <% - JspView me = (JspView) HttpView.currentView(); + JspView me = HttpView.currentView(); NewRequestBean bean = me.getModelBean(); ViewContext context = getViewContext(); Container c = getContainer(); diff --git a/specimen/src/org/labkey/specimen/view/search.jsp b/specimen/src/org/labkey/specimen/view/search.jsp index 5507ef7c..26a8b335 100644 --- a/specimen/src/org/labkey/specimen/view/search.jsp +++ b/specimen/src/org/labkey/specimen/view/search.jsp @@ -35,7 +35,7 @@ } %> <% - JspView me = (JspView) HttpView.currentView(); + JspView me = HttpView.currentView(); int webPartId = me.getModelBean().getWebPartId(); String renderTarget = "labkey-specimen-search-"+ webPartId; %> diff --git a/specimen/src/org/labkey/specimen/view/selectSpecimenProvider.jsp b/specimen/src/org/labkey/specimen/view/selectSpecimenProvider.jsp index 28d6160f..6637c7b0 100644 --- a/specimen/src/org/labkey/specimen/view/selectSpecimenProvider.jsp +++ b/specimen/src/org/labkey/specimen/view/selectSpecimenProvider.jsp @@ -22,7 +22,7 @@ <%@ taglib prefix="labkey" uri="http://www.labkey.org/taglib" %> <%@ page extends="org.labkey.api.jsp.JspBase" %> <% - JspView me = (JspView) HttpView.currentView(); + JspView me = HttpView.currentView(); SelectSpecimenProviderBean bean = me.getModelBean(); %> diff --git a/specimen/src/org/labkey/specimen/view/specimen.jsp b/specimen/src/org/labkey/specimen/view/specimen.jsp index 851ffea9..e2bf2e5a 100644 --- a/specimen/src/org/labkey/specimen/view/specimen.jsp +++ b/specimen/src/org/labkey/specimen/view/specimen.jsp @@ -29,7 +29,7 @@ <%@ page import="org.labkey.specimen.security.permissions.SetSpecimenCommentsPermission" %> <%@ page extends="org.labkey.api.jsp.JspBase" %> <% - JspView me = (JspView) HttpView.currentView(); + JspView me = HttpView.currentView(); SpecimenEventBean bean = me.getModelBean(); Vial vial = bean.getVial(); Location originatingLocation = LocationManager.get().getOriginatingLocation(vial); diff --git a/specimen/src/org/labkey/specimen/view/specimenHeader.jsp b/specimen/src/org/labkey/specimen/view/specimenHeader.jsp index 60224be0..e4bb9964 100644 --- a/specimen/src/org/labkey/specimen/view/specimenHeader.jsp +++ b/specimen/src/org/labkey/specimen/view/specimenHeader.jsp @@ -38,7 +38,7 @@ } %> <% - JspView me = (JspView) HttpView.currentView(); + JspView me = HttpView.currentView(); SpecimenHeaderBean bean = me.getModelBean(); ActionURL createRequestURL = new ActionURL(ShowSearchAction.class, getContainer()); createRequestURL.addParameter("fromGroupedView", !bean.isShowingVials()); diff --git a/specimen/src/org/labkey/specimen/view/specimenVisitReport.jsp b/specimen/src/org/labkey/specimen/view/specimenVisitReport.jsp index 265dd231..f8e56d72 100644 --- a/specimen/src/org/labkey/specimen/view/specimenVisitReport.jsp +++ b/specimen/src/org/labkey/specimen/view/specimenVisitReport.jsp @@ -30,7 +30,7 @@ <%@ page import="java.util.List" %> <%@ page extends="org.labkey.api.jsp.JspBase" %> <% - JspView me = (JspView) HttpView.currentView(); + JspView me = HttpView.currentView(); SpecimenVisitReportParameters bean = me.getModelBean(); Study study = StudyService.get().getStudy(getContainer()); int tableContainerId = getRequestScopedUID(); diff --git a/specimen/src/org/labkey/specimen/view/updateComments.jsp b/specimen/src/org/labkey/specimen/view/updateComments.jsp index cdb33604..aec3a635 100644 --- a/specimen/src/org/labkey/specimen/view/updateComments.jsp +++ b/specimen/src/org/labkey/specimen/view/updateComments.jsp @@ -47,7 +47,7 @@ } %> <% - JspView me = (JspView) HttpView.currentView(); + JspView me = HttpView.currentView(); UpdateSpecimenCommentsBean bean = me.getModelBean(); Container container = getContainer(); diff --git a/specimen/src/org/labkey/specimen/view/uploadSimpleSpecimens.jsp b/specimen/src/org/labkey/specimen/view/uploadSimpleSpecimens.jsp index e225c68d..f97cfcde 100644 --- a/specimen/src/org/labkey/specimen/view/uploadSimpleSpecimens.jsp +++ b/specimen/src/org/labkey/specimen/view/uploadSimpleSpecimens.jsp @@ -26,7 +26,7 @@ <%@ page extends="org.labkey.api.jsp.JspBase" %> <%@ taglib prefix="labkey" uri="http://www.labkey.org/taglib" %> <% - JspView me = (JspView) HttpView.currentView(); + JspView me = HttpView.currentView(); ShowUploadSpecimensAction.UploadSpecimensForm bean = me.getModelBean(); Container c = getContainer(); Study study = StudyService.get().getStudy(c); diff --git a/specimen/src/org/labkey/specimen/view/viewRequestsHeader.jsp b/specimen/src/org/labkey/specimen/view/viewRequestsHeader.jsp index fbfcada4..9c3099b3 100644 --- a/specimen/src/org/labkey/specimen/view/viewRequestsHeader.jsp +++ b/specimen/src/org/labkey/specimen/view/viewRequestsHeader.jsp @@ -23,7 +23,7 @@ <%@ page import="org.labkey.specimen.actions.ViewRequestsHeaderBean" %> <%@ page extends="org.labkey.api.jsp.JspBase" %> <% - JspView me = (JspView) HttpView.currentView(); + JspView me = HttpView.currentView(); ViewContext context = getViewContext(); ViewRequestsHeaderBean bean = me.getModelBean(); ActionURL userLink = context.cloneActionURL(); diff --git a/specimen/src/org/labkey/specimen/view/webPart.jsp b/specimen/src/org/labkey/specimen/view/webPart.jsp index de4c00bb..b0634e84 100644 --- a/specimen/src/org/labkey/specimen/view/webPart.jsp +++ b/specimen/src/org/labkey/specimen/view/webPart.jsp @@ -33,7 +33,8 @@ <%@ page import="org.labkey.specimen.view.SpecimenWebPart" %> <%@ page extends="org.labkey.api.jsp.JspBase" %> <% - SpecimenWebPart.SpecimenWebPartBean bean = (SpecimenWebPart.SpecimenWebPartBean) HttpView.currentView().getModelBean(); + SpecimenWebPart webpart = HttpView.currentView(); + SpecimenWebPart.SpecimenWebPartBean bean = webpart.getModelBean(); Container c = getContainer(); User user = getUser(); From 9bda4eb570ddf9375e8ff83e8d077eb7f9af6747 Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Fri, 11 Apr 2025 14:48:05 -0700 Subject: [PATCH 440/494] Remove support for ancillary studies Original PR: https://github.com/LabKey/platform/pull/6548 --- .../src/org/labkey/specimen/SpecimenRequestManager.java | 2 -- .../labkey/specimen/actions/ReportConfigurationBean.java | 2 +- .../labkey/specimen/importer/AbstractSpecimenTask.java | 9 --------- .../labkey/specimen/view/manageRepositorySettings.jsp | 2 +- 4 files changed, 2 insertions(+), 13 deletions(-) diff --git a/specimen/src/org/labkey/specimen/SpecimenRequestManager.java b/specimen/src/org/labkey/specimen/SpecimenRequestManager.java index 944c8f45..a2479b5a 100644 --- a/specimen/src/org/labkey/specimen/SpecimenRequestManager.java +++ b/specimen/src/org/labkey/specimen/SpecimenRequestManager.java @@ -770,8 +770,6 @@ public void clearCaches(Container c) _requestEventHelper.clearCache(c); _requestHelper.clearCache(c); _requestStatusHelper.clearCache(c); - for (Study study : StudyService.get().getAncillaryStudies(c)) - clearCaches(study.getContainer()); clearGroupedValuesForColumn(c); } diff --git a/specimen/src/org/labkey/specimen/actions/ReportConfigurationBean.java b/specimen/src/org/labkey/specimen/actions/ReportConfigurationBean.java index d12379f8..9cdd98a0 100644 --- a/specimen/src/org/labkey/specimen/actions/ReportConfigurationBean.java +++ b/specimen/src/org/labkey/specimen/actions/ReportConfigurationBean.java @@ -51,7 +51,7 @@ public ReportConfigurationBean(ViewContext viewContext) if (study != null) { boolean enableSpecimenRequest = SettingsManager.get().getRepositorySettings(study.getContainer()).isEnableRequests(); - if (!study.isAncillaryStudy() && !study.isSnapshotStudy() && enableSpecimenRequest) + if (!study.isSnapshotStudy() && enableSpecimenRequest) { registerReportFactory(REQUESTS_BY_DERIVATIVE_TYPE_TITLE, new RequestReportFactory()); registerReportFactory(REQUESTS_BY_DERIVATIVE_TYPE_TITLE, new RequestLocationReportFactory()); diff --git a/specimen/src/org/labkey/specimen/importer/AbstractSpecimenTask.java b/specimen/src/org/labkey/specimen/importer/AbstractSpecimenTask.java index ab8de4cb..4d27ca76 100644 --- a/specimen/src/org/labkey/specimen/importer/AbstractSpecimenTask.java +++ b/specimen/src/org/labkey/specimen/importer/AbstractSpecimenTask.java @@ -168,15 +168,6 @@ public static void doImport(@Nullable Path inputFile, PipelineJob job, SimpleStu { // do any temp file cleanup importHelper.afterImport(ctx); - - // Since changing specimens in this study will impact specimens in ancillary studies dependent on this study, - // we need to force a participant/visit refresh in those study containers (if any): - for (Study dependentStudy : StudyService.get().getAncillaryStudies(ctx.getContainer())) - { - ValidationException errors = VisitService.get().updateParticipantVisits(dependentStudy, ctx.getUser()); - if (errors.hasErrors()) - throw errors; - } } } diff --git a/specimen/src/org/labkey/specimen/view/manageRepositorySettings.jsp b/specimen/src/org/labkey/specimen/view/manageRepositorySettings.jsp index 9816e489..e6b7a6bd 100644 --- a/specimen/src/org/labkey/specimen/view/manageRepositorySettings.jsp +++ b/specimen/src/org/labkey/specimen/view/manageRepositorySettings.jsp @@ -106,7 +106,7 @@ else { %> -

Specimen repository and request settings are not available for ancillary or published studies.

+

Specimen repository and request settings are not available for published studies.

<%= button("Back").href(urlFor(ManageRepositorySettingsAction.class)).onClick("window.history.back();return false;") %> <% } From e987adc8bcc267791d0b1ec4be947cb21e484e5c Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Sun, 13 Apr 2025 13:24:28 -0700 Subject: [PATCH 441/494] Improve display element builders Original PR: https://github.com/LabKey/platform/pull/6558 --- .../SpecimenCommentAuditProvider.java | 4 ++-- .../specimen/actions/ManageRequestBean.java | 4 ++-- .../specimen/actions/SpecimenController.java | 14 ++++++------ .../specimen/query/SpecimenQueryView.java | 3 ++- .../report/SpecimenVisitReportParameters.java | 10 ++++----- .../ParticipantTypeReportFactory.java | 22 ++++++++----------- .../request/BaseRequestReportFactory.java | 10 ++++----- .../request/RequestLocationReportFactory.java | 10 ++++----- .../specimentype/TypeCohortReportFactory.java | 13 +++++------ .../specimentype/TypeReportFactory.java | 8 +++---- ...cimenRequestNotificationEmailTemplate.java | 4 ++-- .../labkey/specimen/view/chooseImporter.jsp | 8 +++---- .../labkey/specimen/view/requestSpecimens.jsp | 2 +- 13 files changed, 54 insertions(+), 58 deletions(-) diff --git a/specimen/src/org/labkey/specimen/SpecimenCommentAuditProvider.java b/specimen/src/org/labkey/specimen/SpecimenCommentAuditProvider.java index 638d46cb..cc3becb4 100644 --- a/specimen/src/org/labkey/specimen/SpecimenCommentAuditProvider.java +++ b/specimen/src/org/labkey/specimen/SpecimenCommentAuditProvider.java @@ -31,7 +31,7 @@ import org.labkey.api.data.TableInfo; import org.labkey.api.query.FieldKey; import org.labkey.api.query.UserSchema; -import org.labkey.api.util.Link; +import org.labkey.api.util.LinkBuilder; import org.labkey.api.view.ActionURL; import org.labkey.api.writer.HtmlWriter; import org.labkey.specimen.actions.SpecimenController; @@ -141,7 +141,7 @@ public void renderGridCellContents(RenderContext ctx, Writer oldWriter, HtmlWrit } ActionURL url = SpecimenController.getCommentURL(container, globalUniqueId); - oldWriter.write(new Link.LinkBuilder(globalUniqueId).href(url).clearClasses().toString()); + oldWriter.write(LinkBuilder.simpleLink(globalUniqueId, url).toString()); } }; } diff --git a/specimen/src/org/labkey/specimen/actions/ManageRequestBean.java b/specimen/src/org/labkey/specimen/actions/ManageRequestBean.java index 0732f687..759d4907 100644 --- a/specimen/src/org/labkey/specimen/actions/ManageRequestBean.java +++ b/specimen/src/org/labkey/specimen/actions/ManageRequestBean.java @@ -7,7 +7,7 @@ import org.labkey.api.specimen.location.LocationImpl; import org.labkey.api.specimen.location.LocationManager; import org.labkey.api.study.Location; -import org.labkey.api.util.Button; +import org.labkey.api.util.ButtonBuilder; import org.labkey.api.view.ActionURL; import org.labkey.api.view.DisplayElement; import org.labkey.api.view.ViewContext; @@ -83,7 +83,7 @@ public ManageRequestBean(ViewContext context, SpecimenRequest specimenRequest, b ActionURL importActionURL = new ActionURL(ImportVialIdsAction.class, _container); importActionURL.addParameter("id", specimenRequest.getRowId()); - Button importButton = new Button.ButtonBuilder("Upload Specimen Ids") + ButtonBuilder.Button importButton = new ButtonBuilder("Upload Specimen Ids") .href(importActionURL) .submit(false) .build(); diff --git a/specimen/src/org/labkey/specimen/actions/SpecimenController.java b/specimen/src/org/labkey/specimen/actions/SpecimenController.java index 255e6bc6..94674b80 100644 --- a/specimen/src/org/labkey/specimen/actions/SpecimenController.java +++ b/specimen/src/org/labkey/specimen/actions/SpecimenController.java @@ -109,19 +109,19 @@ import org.labkey.api.study.model.CohortService; import org.labkey.api.study.model.ParticipantDataset; import org.labkey.api.study.security.permissions.ManageStudyPermission; -import org.labkey.api.util.Button; +import org.labkey.api.util.ButtonBuilder; import org.labkey.api.util.ConfigurationException; import org.labkey.api.util.DateUtil; import org.labkey.api.util.ExceptionUtil; import org.labkey.api.util.FileStream; import org.labkey.api.util.GUID; -import org.labkey.api.util.Link; +import org.labkey.api.util.LinkBuilder; import org.labkey.api.util.MailHelper; import org.labkey.api.util.PageFlowUtil; import org.labkey.api.util.Pair; import org.labkey.api.util.URLHelper; -import org.labkey.api.util.element.Option.OptionBuilder; -import org.labkey.api.util.element.Select; +import org.labkey.api.util.OptionBuilder; +import org.labkey.api.util.SelectBuilder; import org.labkey.api.util.emailTemplate.EmailTemplateService; import org.labkey.api.view.ActionURL; import org.labkey.api.view.AjaxCompletion; @@ -532,7 +532,7 @@ public SpecimenQueryView getSpecimenQueryView(boolean showVials, boolean forExpo if (getViewContext().hasPermission(AdminPermission.class)) { - Button upload = new Button.ButtonBuilder("Import Specimens") + ButtonBuilder.Button upload = new ButtonBuilder("Import Specimens") .href(new ActionURL(ShowUploadSpecimensAction.class, getContainer())) .build(); buttons.add(upload); @@ -562,7 +562,7 @@ public void render(RenderContext ctx, Writer out) throws IOException int locationId = null == StringUtils.trimToNull(excludeStr) ? 0 : Integer.parseInt(excludeStr); List locations = LocationManager.get().getValidRequestingLocations(ctx.getContainer()); - new Select.SelectBuilder() + new SelectBuilder() .addOption("", "") .addOptions(locations.stream().map(location -> new OptionBuilder(location.getDisplayName(), location.getRowId()))) .selected(locationId) @@ -4387,7 +4387,7 @@ public class RequestHistoryAction extends SimpleViewAction public ModelAndView getView(IdForm form, BindException errors) { _requestId = form.getId(); - HtmlView header = new HtmlView(new Link.LinkBuilder("View Request").href(SpecimenController.getManageRequestURL(getContainer(), form.getId(), null))); + HtmlView header = new HtmlView(new LinkBuilder("View Request").href(SpecimenController.getManageRequestURL(getContainer(), form.getId(), null))); SimpleFilter filter = new SimpleFilter(FieldKey.fromParts("RequestId"), form.getId()); GridView historyGrid = getRequestEventGridView(getViewContext().getRequest(), errors, filter); return new VBox(header, historyGrid); diff --git a/specimen/src/org/labkey/specimen/query/SpecimenQueryView.java b/specimen/src/org/labkey/specimen/query/SpecimenQueryView.java index 9adbd10c..93b3e6f1 100644 --- a/specimen/src/org/labkey/specimen/query/SpecimenQueryView.java +++ b/specimen/src/org/labkey/specimen/query/SpecimenQueryView.java @@ -59,6 +59,7 @@ import org.labkey.api.study.model.ParticipantDataset; import org.labkey.api.util.HtmlString; import org.labkey.api.util.HtmlStringBuilder; +import org.labkey.api.util.LinkBuilder; import org.labkey.api.util.PageFlowUtil; import org.labkey.api.view.ActionURL; import org.labkey.api.view.DataView; @@ -188,7 +189,7 @@ protected void renderExtraRecordSelectorContent(RenderContext ctx, HtmlWriter ou HtmlStringBuilder builder = HtmlStringBuilder.of(reason instanceof String r ? r : "Specimen Unavailable.") .append(HtmlString.BR).append(HtmlString.BR) .append("Click ") - .append(PageFlowUtil.link("[history]", getHistoryLink(ctx)).clearClasses()) + .append(LinkBuilder.simpleLink("[history]", getHistoryLink(ctx))) .append(" for more information."); out.write(PageFlowUtil.popupHelp(builder.getHtmlString(), "Specimen Unavailable")); diff --git a/specimen/src/org/labkey/specimen/report/SpecimenVisitReportParameters.java b/specimen/src/org/labkey/specimen/report/SpecimenVisitReportParameters.java index cd523202..fea8b56d 100644 --- a/specimen/src/org/labkey/specimen/report/SpecimenVisitReportParameters.java +++ b/specimen/src/org/labkey/specimen/report/SpecimenVisitReportParameters.java @@ -34,8 +34,8 @@ import org.labkey.api.util.PageFlowUtil; import org.labkey.api.util.Pair; import org.labkey.api.util.SafeToRenderEnum; -import org.labkey.api.util.element.Option.OptionBuilder; -import org.labkey.api.util.element.Select; +import org.labkey.api.util.OptionBuilder; +import org.labkey.api.util.SelectBuilder; import org.labkey.api.view.ActionURL; import org.labkey.api.view.UnauthorizedException; import org.labkey.api.view.ViewForm; @@ -335,7 +335,7 @@ else if (cohortFilter != null) protected Pair getEnrollmentSitePicker(String inputName, Set locations, Integer selectedSiteId) { - Select.SelectBuilder sb = new Select.SelectBuilder() + SelectBuilder sb = new SelectBuilder() .name(inputName) .addOption(new OptionBuilder() .value("") @@ -369,7 +369,7 @@ protected Pair getEnrollmentSitePicker(String inputName, Set public HtmlString getCustomViewPicker(Map specimenDetailViews) { - Select.SelectBuilder sb = new Select.SelectBuilder() + SelectBuilder sb = new SelectBuilder() .name("baseCustomViewName") .addOption(new OptionBuilder() .value("") @@ -427,7 +427,7 @@ public boolean isAllSubjectsOption(String subject) protected Pair getParticipantPicker(String inputName, String selectedParticipantId) { Study study = StudyService.get().getStudy(getContainer()); - Select.SelectBuilder builder = new Select.SelectBuilder(); + SelectBuilder builder = new SelectBuilder(); String allString = getAllString(); Collection participantIds = StudyService.get().getParticipantIds(study, getUser()); diff --git a/specimen/src/org/labkey/specimen/report/participant/ParticipantTypeReportFactory.java b/specimen/src/org/labkey/specimen/report/participant/ParticipantTypeReportFactory.java index a854c08a..3ad79ee5 100644 --- a/specimen/src/org/labkey/specimen/report/participant/ParticipantTypeReportFactory.java +++ b/specimen/src/org/labkey/specimen/report/participant/ParticipantTypeReportFactory.java @@ -18,15 +18,15 @@ import org.labkey.api.data.CompareType; import org.labkey.api.data.SimpleFilter; import org.labkey.api.security.User; -import org.labkey.specimen.model.SpecimenTypeSummary; import org.labkey.api.study.StudyService; import org.labkey.api.study.Visit; import org.labkey.api.util.HtmlString; +import org.labkey.api.util.OptionBuilder; import org.labkey.api.util.Pair; -import org.labkey.api.util.element.Option; -import org.labkey.api.util.element.Select; +import org.labkey.api.util.SelectBuilder; import org.labkey.specimen.SpecimenManager; import org.labkey.specimen.actions.SpecimenReportActions; +import org.labkey.specimen.model.SpecimenTypeSummary; import org.labkey.specimen.report.SpecimenVisitReport; import org.labkey.specimen.report.SpecimenVisitReportAction; import org.labkey.specimen.report.SpecimenVisitReportParameters; @@ -37,10 +37,6 @@ import static org.labkey.api.util.HtmlString.unsafe; -/** - * User: brittp - * Created: Jan 30, 2008 3:40:07 PM - */ public class ParticipantTypeReportFactory extends SpecimenVisitReportParameters { private String _selectedType; @@ -81,7 +77,7 @@ public List> getAdditionalFormInputHtml(User user) return Collections.singletonList(getSpecimenTypePicker()); } - private void appendOptions(List types, Select.SelectBuilder builder, String parentId, String selectedId, int indent) + private void appendOptions(List types, SelectBuilder builder, String parentId, String selectedId, int indent) { for (SpecimenTypeSummary.TypeCount type : types) { @@ -90,7 +86,7 @@ private void appendOptions(List types, HtmlString encodedLabel = unsafe(spacing + HtmlString.of(label)); String id = parentId != null ? parentId + TYPE_COMPONENT_SEPARATOR + label : label; - builder.addOption(new Option.OptionBuilder() + builder.addOption(new OptionBuilder() .value(id) .label(encodedLabel) .selected(id.equals(selectedId)) @@ -171,23 +167,23 @@ private SpecimenTypeSummary.TypeCount getTypeCountByLabel(List getSpecimenTypePicker() { - Select.SelectBuilder select = new Select.SelectBuilder(); + SelectBuilder select = new SelectBuilder(); SpecimenTypeSummary summary = SpecimenManager.get().getSpecimenTypeSummary(getContainer(), getUser()); select.name("selectedType"); - select.addOption(new Option.OptionBuilder() + select.addOption(new OptionBuilder() .value(ALL_PRIMARY_TYPES_FORM_VALUE) .label("One report per primary type") .selected(ALL_PRIMARY_TYPES_FORM_VALUE.equals(_selectedType)) .build()); - select.addOption(new Option.OptionBuilder() + select.addOption(new OptionBuilder() .value(ALL_DERIVATIVE_TYPES_FORM_VALUE) .label("One report per derivative type") .selected(ALL_DERIVATIVE_TYPES_FORM_VALUE.equals(_selectedType)) .build()); - select.addOption(new Option.OptionBuilder() + select.addOption(new OptionBuilder() .value(ALL_ADDITIVE_TYPES_FORM_VALUE) .label("One report per additive type") .selected(ALL_ADDITIVE_TYPES_FORM_VALUE.equals(_selectedType)) diff --git a/specimen/src/org/labkey/specimen/report/request/BaseRequestReportFactory.java b/specimen/src/org/labkey/specimen/report/request/BaseRequestReportFactory.java index 3bda3972..49285411 100644 --- a/specimen/src/org/labkey/specimen/report/request/BaseRequestReportFactory.java +++ b/specimen/src/org/labkey/specimen/report/request/BaseRequestReportFactory.java @@ -18,8 +18,8 @@ import org.labkey.api.security.User; import org.labkey.api.util.HtmlString; import org.labkey.api.util.Pair; -import org.labkey.api.util.element.Option; -import org.labkey.api.util.element.Select; +import org.labkey.api.util.OptionBuilder; +import org.labkey.api.util.SelectBuilder; import org.labkey.specimen.report.specimentype.TypeReportFactory; import java.util.List; @@ -50,15 +50,15 @@ public List> getAdditionalFormInputHtml(User user) { List> superInputs = super.getAdditionalFormInputHtml(user); - Select.SelectBuilder sb = new Select.SelectBuilder(); + SelectBuilder sb = new SelectBuilder(); - sb.addOption(new Option.OptionBuilder() + sb.addOption(new OptionBuilder() .value(Boolean.toString(false)) .label("Include in-process requests") .build() ); - sb.addOption(new Option.OptionBuilder() + sb.addOption(new OptionBuilder() .value(Boolean.toString(true)) .label("Completed requests only") .selected(_completedRequestsOnly) diff --git a/specimen/src/org/labkey/specimen/report/request/RequestLocationReportFactory.java b/specimen/src/org/labkey/specimen/report/request/RequestLocationReportFactory.java index 7a035388..9110aa7b 100644 --- a/specimen/src/org/labkey/specimen/report/request/RequestLocationReportFactory.java +++ b/specimen/src/org/labkey/specimen/report/request/RequestLocationReportFactory.java @@ -23,8 +23,8 @@ import org.labkey.api.study.Visit; import org.labkey.api.util.HtmlString; import org.labkey.api.util.Pair; -import org.labkey.api.util.element.Option; -import org.labkey.api.util.element.Select; +import org.labkey.api.util.OptionBuilder; +import org.labkey.api.util.SelectBuilder; import org.labkey.specimen.SpecimenManager; import org.labkey.specimen.actions.SpecimenReportActions; import org.labkey.specimen.report.SpecimenVisitReport; @@ -120,17 +120,17 @@ public Class getAction() public List> getAdditionalFormInputHtml(User user) { - Select.SelectBuilder builder = new Select.SelectBuilder(); + SelectBuilder builder = new SelectBuilder(); builder.name("locationId") - .addOption(new Option.OptionBuilder() + .addOption(new OptionBuilder() .value("") .label("All Requesting Locations") .build()); for (LocationImpl location : SpecimenManager.get().getSitesWithRequests(getContainer())) { - builder.addOption(new Option.OptionBuilder() + builder.addOption(new OptionBuilder() .value(Integer.toString(location.getRowId())) .label(location.getLabel()) .selected(_locationId != null && location.getRowId() == _locationId) diff --git a/specimen/src/org/labkey/specimen/report/specimentype/TypeCohortReportFactory.java b/specimen/src/org/labkey/specimen/report/specimentype/TypeCohortReportFactory.java index d3e54bee..2d33b91c 100644 --- a/specimen/src/org/labkey/specimen/report/specimentype/TypeCohortReportFactory.java +++ b/specimen/src/org/labkey/specimen/report/specimentype/TypeCohortReportFactory.java @@ -26,9 +26,9 @@ import org.labkey.api.study.model.CohortService; import org.labkey.api.util.HtmlString; import org.labkey.api.util.Pair; -import org.labkey.api.util.element.Input; -import org.labkey.api.util.element.Option; -import org.labkey.api.util.element.Select; +import org.labkey.api.util.InputBuilder; +import org.labkey.api.util.OptionBuilder; +import org.labkey.api.util.SelectBuilder; import org.labkey.specimen.SpecimenManager; import org.labkey.specimen.actions.SpecimenReportActions; import org.labkey.specimen.report.SpecimenVisitReport; @@ -65,17 +65,16 @@ public List> getAdditionalFormInputHtml(User user) if (study.isAdvancedCohorts()) { CohortFilter.Type currentType = getCohortFilter() != null ? getCohortFilter().getType() : CohortFilter.Type.DATA_COLLECTION; - Input.InputBuilder input = new Input.InputBuilder<>() - .type("hidden") + InputBuilder input = InputBuilder.hidden() .value("0") .name(Params.cohortId.name()); - Select.SelectBuilder select = new Select.SelectBuilder() + SelectBuilder select = new SelectBuilder() .name(Params.cohortFilterType.name()); for (CohortFilter.Type type : CohortFilter.Type.values()) { - select.addOption(new Option.OptionBuilder() + select.addOption(new OptionBuilder() .value(type.name()) .label(type.getTitle()) .selected(type == currentType) diff --git a/specimen/src/org/labkey/specimen/report/specimentype/TypeReportFactory.java b/specimen/src/org/labkey/specimen/report/specimentype/TypeReportFactory.java index 72a789ef..ac1d53bc 100644 --- a/specimen/src/org/labkey/specimen/report/specimentype/TypeReportFactory.java +++ b/specimen/src/org/labkey/specimen/report/specimentype/TypeReportFactory.java @@ -19,8 +19,8 @@ import org.labkey.api.study.SpecimenService; import org.labkey.api.util.HtmlString; import org.labkey.api.util.Pair; -import org.labkey.api.util.element.Option; -import org.labkey.api.util.element.Select; +import org.labkey.api.util.OptionBuilder; +import org.labkey.api.util.SelectBuilder; import org.labkey.specimen.report.SpecimenTypeLevel; import org.labkey.specimen.report.SpecimenVisitReportParameters; @@ -42,12 +42,12 @@ public List> getAdditionalFormInputHtml(User user) if (!SpecimenService.get().getRequestCustomizer().omitTypeGroupingsWhenReporting()) { - Select.SelectBuilder builder = new Select.SelectBuilder(); + SelectBuilder builder = new SelectBuilder(); builder.name(PARAMS.typeLevel.name()); for (SpecimenTypeLevel level : SpecimenTypeLevel.values()) { - builder.addOption(new Option.OptionBuilder() + builder.addOption(new OptionBuilder() .value(level.name()) .label("Show results by: " + level.getLabel()) .selected(getTypeLevelEnum() == level) diff --git a/specimen/src/org/labkey/specimen/view/SpecimenRequestNotificationEmailTemplate.java b/specimen/src/org/labkey/specimen/view/SpecimenRequestNotificationEmailTemplate.java index df3de869..1c91886c 100644 --- a/specimen/src/org/labkey/specimen/view/SpecimenRequestNotificationEmailTemplate.java +++ b/specimen/src/org/labkey/specimen/view/SpecimenRequestNotificationEmailTemplate.java @@ -20,7 +20,7 @@ import org.labkey.api.security.User; import org.labkey.api.study.Study; import org.labkey.api.study.StudyService; -import org.labkey.api.util.Link.LinkBuilder; +import org.labkey.api.util.LinkBuilder; import org.labkey.api.util.PageFlowUtil; import org.labkey.api.util.emailTemplate.EmailTemplate; import org.labkey.specimen.actions.SpecimenController; @@ -159,7 +159,7 @@ protected void addCustomReplacements(Replacements replacements) StringBuilder sb = new StringBuilder(); for (Attachment att : attachments) { - sb.append(new LinkBuilder(att.getName()).href(SpecimenController.getDownloadURL(_notification.getEvent(), att.getName())).clearClasses()); + sb.append(LinkBuilder.simpleLink(att.getName(), SpecimenController.getDownloadURL(_notification.getEvent(), att.getName()))); sb.append("
"); } return sb.toString(); diff --git a/specimen/src/org/labkey/specimen/view/chooseImporter.jsp b/specimen/src/org/labkey/specimen/view/chooseImporter.jsp index 3b25f85b..6cc91050 100644 --- a/specimen/src/org/labkey/specimen/view/chooseImporter.jsp +++ b/specimen/src/org/labkey/specimen/view/chooseImporter.jsp @@ -12,7 +12,7 @@ <%@ page import="org.labkey.api.study.SpecimenService" %> <%@ page import="org.labkey.api.study.SpecimenTransform" %> <%@ page import="org.labkey.api.study.StudyUrls" %> -<%@ page import="org.labkey.api.util.Button" %> +<%@ page import="org.labkey.api.util.ButtonBuilder" %> <%@ page import="org.labkey.api.util.HtmlString" %> <%@ page import="org.labkey.api.util.URLHelper" %> <%@ page import="org.labkey.api.view.ActionURL" %> @@ -103,12 +103,12 @@
- <%= new Button.ButtonBuilder("Save") + <%= new ButtonBuilder("Save") .submit(true) .build() %> - <%= new Button.ButtonBuilder("Cancel") + <%= new ButtonBuilder("Cancel") .href(cancelLink) .build() %> @@ -131,7 +131,7 @@

- <%= new Button.ButtonBuilder("Done") + <%= new ButtonBuilder("Done") .href(cancelLink) .build() %> diff --git a/specimen/src/org/labkey/specimen/view/requestSpecimens.jsp b/specimen/src/org/labkey/specimen/view/requestSpecimens.jsp index d575a666..335a0217 100644 --- a/specimen/src/org/labkey/specimen/view/requestSpecimens.jsp +++ b/specimen/src/org/labkey/specimen/view/requestSpecimens.jsp @@ -20,7 +20,7 @@ <%@ page import="org.labkey.api.specimen.location.LocationImpl"%> <%@ page import="org.labkey.api.specimen.location.LocationManager"%> <%@ page import="org.labkey.api.study.SpecimenService"%> -<%@ page import="org.labkey.api.util.element.Option.OptionBuilder"%> +<%@ page import="org.labkey.api.util.OptionBuilder"%> <%@ page import="org.labkey.api.view.ActionURL"%> <%@ page import="org.labkey.api.view.HttpView"%> <%@ page import="org.labkey.api.view.JspView" %> From 344718340137befb826b2f836c91683c2bc6c33e Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Mon, 14 Apr 2025 15:49:32 -0700 Subject: [PATCH 442/494] Simplify and expand use of LinkBuilder factory methods Original PR: https://github.com/LabKey/platform/pull/6561 --- .../labkey/specimen/actions/ShowUploadSpecimensAction.java | 5 +++-- .../src/org/labkey/specimen/actions/SpecimenController.java | 6 +++--- .../notifications/ActorNotificationRecipientSet.java | 3 ++- specimen/src/org/labkey/specimen/view/labSpecimenLists.jsp | 4 ++-- specimen/src/org/labkey/specimen/view/manageActors.jsp | 2 +- specimen/src/org/labkey/specimen/view/manageRequirement.jsp | 2 +- specimen/src/org/labkey/specimen/view/manageStatuses.jsp | 2 +- specimen/src/org/labkey/specimen/view/specimen.jsp | 4 ++-- specimen/src/org/labkey/specimen/view/specimenHeader.jsp | 2 +- 9 files changed, 16 insertions(+), 14 deletions(-) diff --git a/specimen/src/org/labkey/specimen/actions/ShowUploadSpecimensAction.java b/specimen/src/org/labkey/specimen/actions/ShowUploadSpecimensAction.java index 8cb118d7..ea53ca90 100644 --- a/specimen/src/org/labkey/specimen/actions/ShowUploadSpecimensAction.java +++ b/specimen/src/org/labkey/specimen/actions/ShowUploadSpecimensAction.java @@ -31,6 +31,7 @@ import org.labkey.api.security.RequiresPermission; import org.labkey.api.security.User; import org.labkey.api.security.permissions.AdminPermission; +import org.labkey.api.util.LinkBuilder; import org.labkey.specimen.importer.SimpleSpecimenImporter; import org.labkey.specimen.settings.RepositorySettings; import org.labkey.api.study.Study; @@ -314,9 +315,9 @@ public ModelAndView getView(Object o, BindException errors) return new HtmlView( HtmlStringBuilder.of("Specimens uploaded successfully.") .append(HtmlString.BR).append(HtmlString.BR) - .append(PageFlowUtil.link("study home", homeLink)) + .append(LinkBuilder.labkeyLink("study home", homeLink)) .append(" ") - .append(PageFlowUtil.link("specimens", samplesLink) + .append(LinkBuilder.labkeyLink("specimens", samplesLink) ) ); } diff --git a/specimen/src/org/labkey/specimen/actions/SpecimenController.java b/specimen/src/org/labkey/specimen/actions/SpecimenController.java index 94674b80..52650107 100644 --- a/specimen/src/org/labkey/specimen/actions/SpecimenController.java +++ b/specimen/src/org/labkey/specimen/actions/SpecimenController.java @@ -4099,8 +4099,8 @@ public ModelAndView getView(UpdateParticipantCommentsForm specimenCommentsForm, errors.reject(ERROR_MSG, e.getMessage()); } } - if (selectedVials == null || selectedVials.size() == 0) - return HtmlView.unsafe("No vials selected. " + PageFlowUtil.link("back").onClick("back()")); + if (selectedVials == null || selectedVials.isEmpty()) + return HtmlView.unsafe("No vials selected. " + LinkBuilder.labkeyLink("back").onClick("back()")); } return new JspView<>("/org/labkey/specimen/view/updateComments.jsp", @@ -4387,7 +4387,7 @@ public class RequestHistoryAction extends SimpleViewAction public ModelAndView getView(IdForm form, BindException errors) { _requestId = form.getId(); - HtmlView header = new HtmlView(new LinkBuilder("View Request").href(SpecimenController.getManageRequestURL(getContainer(), form.getId(), null))); + HtmlView header = new HtmlView(LinkBuilder.labkeyLink("View Request", SpecimenController.getManageRequestURL(getContainer(), form.getId(), null))); SimpleFilter filter = new SimpleFilter(FieldKey.fromParts("RequestId"), form.getId()); GridView historyGrid = getRequestEventGridView(getViewContext().getRequest(), errors, filter); return new VBox(header, historyGrid); diff --git a/specimen/src/org/labkey/specimen/notifications/ActorNotificationRecipientSet.java b/specimen/src/org/labkey/specimen/notifications/ActorNotificationRecipientSet.java index c7086f25..ef1709d3 100644 --- a/specimen/src/org/labkey/specimen/notifications/ActorNotificationRecipientSet.java +++ b/specimen/src/org/labkey/specimen/notifications/ActorNotificationRecipientSet.java @@ -22,6 +22,7 @@ import org.labkey.api.specimen.location.LocationManager; import org.labkey.api.util.HtmlString; import org.labkey.api.util.HtmlStringBuilder; +import org.labkey.api.util.LinkBuilder; import org.labkey.api.util.PageFlowUtil; import org.labkey.api.util.SafeToRender; import org.labkey.api.view.ActionURL; @@ -109,7 +110,7 @@ private HtmlString getConfigureEmailsLinkHTML(ActionURL currentUrl) getLocation() != null ? getLocation().getRowId() : null, currentUrl ); - return PageFlowUtil.link("Configure Addresses").href(url).getHtmlString(); + return LinkBuilder.labkeyLink("Configure Addresses", url).getHtmlString(); } public SafeToRender getHtmlDescriptionAndLink(boolean hasEmailAddresses, ActionURL currentUrl) diff --git a/specimen/src/org/labkey/specimen/view/labSpecimenLists.jsp b/specimen/src/org/labkey/specimen/view/labSpecimenLists.jsp index 2a082050..ebee55b5 100644 --- a/specimen/src/org/labkey/specimen/view/labSpecimenLists.jsp +++ b/specimen/src/org/labkey/specimen/view/labSpecimenLists.jsp @@ -72,10 +72,10 @@
- + - +
<%=link("Export to Excel").href(downloadURL.replaceParameter("export", "xls"))%><%=link("Export to Excel", downloadURL.replaceParameter("export", "xls"))%>
<%=link("Export to text file").href(downloadURL.replaceParameter("export", "tsv")) %><%=link("Export to text file", downloadURL.replaceParameter("export", "tsv")) %>

diff --git a/specimen/src/org/labkey/specimen/view/manageActors.jsp b/specimen/src/org/labkey/specimen/view/manageActors.jsp index e2843ab1..da39822f 100644 --- a/specimen/src/org/labkey/specimen/view/manageActors.jsp +++ b/specimen/src/org/labkey/specimen/view/manageActors.jsp @@ -104,7 +104,7 @@ } else { - %><%= link("Update Members").href(updateMembersLink) %><% + %><%= link("Update Members", updateMembersLink) %><% if (!inUseActorIds.contains(actor.getRowId())) { %><%=link("Delete").onClick("return LABKEY.Utils.confirmAndPost('Deleting this actor will delete all information about its membership. All member emails will need to be entered again if you recreate this actor.', '" + h(urlFor(DeleteActorAction.class).addParameter("id", actor.getRowId())) + "')") %><% diff --git a/specimen/src/org/labkey/specimen/view/manageRequirement.jsp b/specimen/src/org/labkey/specimen/view/manageRequirement.jsp index 4057fa68..68109262 100644 --- a/specimen/src/org/labkey/specimen/view/manageRequirement.jsp +++ b/specimen/src/org/labkey/specimen/view/manageRequirement.jsp @@ -49,7 +49,7 @@ - + <% if (bean.isRequestManager() && bean.isFinalState()) diff --git a/specimen/src/org/labkey/specimen/view/manageStatuses.jsp b/specimen/src/org/labkey/specimen/view/manageStatuses.jsp index 05d88773..7fc51da3 100644 --- a/specimen/src/org/labkey/specimen/view/manageStatuses.jsp +++ b/specimen/src/org/labkey/specimen/view/manageStatuses.jsp @@ -102,7 +102,7 @@ function showSystemRows(value) else { %> - + <% } %> diff --git a/specimen/src/org/labkey/specimen/view/specimen.jsp b/specimen/src/org/labkey/specimen/view/specimen.jsp index e2bf2e5a..97327250 100644 --- a/specimen/src/org/labkey/specimen/view/specimen.jsp +++ b/specimen/src/org/labkey/specimen/view/specimen.jsp @@ -97,10 +97,10 @@ if (comment != null) <% } } -if (bean.getReturnUrl() != null && bean.getReturnUrl().length() > 0) +if (bean.getReturnUrl() != null && !bean.getReturnUrl().isEmpty()) { %> -
<%=link("return to vial view").href(bean.getReturnUrl() )%> +
<%=link("return to vial view", bean.getReturnUrl() )%> <% } %> diff --git a/specimen/src/org/labkey/specimen/view/specimenHeader.jsp b/specimen/src/org/labkey/specimen/view/specimenHeader.jsp index e4bb9964..b0599197 100644 --- a/specimen/src/org/labkey/specimen/view/specimenHeader.jsp +++ b/specimen/src/org/labkey/specimen/view/specimenHeader.jsp @@ -71,7 +71,7 @@ } %> <%=link(vialLinkText, bean.getOtherViewURL())%>  -<%=link("Search").href(ShowSearchAction.getShowSearchURL(getContainer(), bean.isShowingVials()))%>  +<%=link("Search", ShowSearchAction.getShowSearchURL(getContainer(), bean.isShowingVials()))%>  <%=link("Reports", urlFor(SpecimenController.AutoReportListAction.class)) %> <% if (!bean.getFilteredPtidVisits().isEmpty()) From d44d26431175698176ad5c82686e4d15f7586e92 Mon Sep 17 00:00:00 2001 From: Xing Yang <5168106+XingY@users.noreply.github.com> Date: Tue, 22 Apr 2025 11:13:49 -0700 Subject: [PATCH 443/494] Issue 52728: LKSM: Update from file with duplicate rows is only updating once. Original PR: https://github.com/LabKey/platform/pull/6571 --- .../org/labkey/specimen/query/SpecimenUpdateService.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/specimen/src/org/labkey/specimen/query/SpecimenUpdateService.java b/specimen/src/org/labkey/specimen/query/SpecimenUpdateService.java index 7d3f9682..d7285c9a 100644 --- a/specimen/src/org/labkey/specimen/query/SpecimenUpdateService.java +++ b/specimen/src/org/labkey/specimen/query/SpecimenUpdateService.java @@ -340,6 +340,14 @@ public List> updateRows(User user, Container container, List Map row = rows.get(i); Map keys = oldKeys == null ? row : oldKeys.get(i); long rowId = keyFromMap(keys); + try + { + checkDuplicateUpdate(rowId); + } + catch (ValidationException e) + { + throw new BatchValidationException(e); + } rowIds.add(rowId); uniqueRows.put(rowId, row); } From 6bcdfac990679d7e1d1f05d7de0b672e162a28b2 Mon Sep 17 00:00:00 2001 From: labkey-matthewb Date: Thu, 1 May 2025 14:27:50 -0700 Subject: [PATCH 444/494] DatabaseIdentifier Original PR: https://github.com/LabKey/platform/pull/6498 * I don't think we need placeholder Results(null) * null check * DatabaseIdentifier ColumnInfo.getAlias() ColumnInfo.getSelectName() * DatabaseIdentifier * use SimpleFilter.getSQLFragment(tableInfo) * TableInfo.getMetaDataName() * comment * rm _defaultTableInfo * sweep for usages of makeLegalIdentiier() and append(alias) * DatabaseIdentifier.getString() -> getid() some fuzz testing * more fuzz testing fix double-quoting (makeLegalIdentifier) of names in specimen land * fix exp.xml * fix un-tabled columinfo * oops * sql generation * makeDatabaseIdentifier() * oor * !equals() * search for "+ col.getAlias() +" (implicit .toString()) * support STR_TABLE_ALIAS in SimpleFilter.SQLClause * support STR_TABLE_ALIAS in SimpleFilter.SQLClause * SampleDatasetTable fix * getProperties() * newCohortLabel * replace .get(column.getAlias()) * rm assert * StringBuilder -> SQLFragment * createMetaDataNameMap() * SQLClause reorg * aggregateResults map is weird * StringBuilder->SQLFragment also assert in DatabaseIdentifier.toString() to find teamcity failures * alias.toString() * StringBuilder->SQLFragment * test fix? * rename get*Name() -> get*Identifier() * this one weird trick will fix your MS2Test * generate alias for not-in-database columninfo * Fix merge conflicts * columnName -> columnNameFragment * Comments and visibility * Mostly spelling * remove AntTask debug hack * Imports * Merge fb_long_identifiers changes into fb_databaseidentifier Original PR: https://github.com/LabKey/platform/pull/6616 * getAlias().getId() * @NotNull SqlDialect * More cleanup * CTE(SqlDialect) * Test fix * patch up missing dialect in ScriptEngineReport temporary fix * comment --------- Co-authored-by: Adam Rauch --- .../specimen/SpecimenRequestManager.java | 29 ++--- .../specimen/actions/SpecimenController.java | 25 ---- .../specimen/importer/ImportableColumn.java | 5 +- .../specimen/importer/SpecimenImporter.java | 107 +++++++++--------- .../specimen/query/SpecimenQueryView.java | 12 +- .../report/SpecimenReportManager.java | 4 +- .../report/SpecimenVisitReportParameters.java | 7 +- .../specimen/writer/SpecimenWriter.java | 14 +-- 8 files changed, 93 insertions(+), 110 deletions(-) diff --git a/specimen/src/org/labkey/specimen/SpecimenRequestManager.java b/specimen/src/org/labkey/specimen/SpecimenRequestManager.java index a2479b5a..5c94fa55 100644 --- a/specimen/src/org/labkey/specimen/SpecimenRequestManager.java +++ b/specimen/src/org/labkey/specimen/SpecimenRequestManager.java @@ -703,8 +703,8 @@ private void updateVialCounts(Container container, User user, List vials) ColumnInfo column = tableInfoSpecimen.getColumn(rollupItem.first); if (null == column) throw new IllegalStateException("Expected Specimen table column to exist."); - String colSelectName = column.getSelectName(); - updateSql.append(",\n ").append(colSelectName).append(" = VialCounts.").append(colSelectName); + var colSelectName = column.getSelectIdentifier(); + updateSql.append(",\n ").appendIdentifier(colSelectName).append(" = VialCounts.").appendIdentifier(colSelectName); } updateSql.append(UPDATE_SPECIMEN_SELECTS); @@ -720,16 +720,17 @@ private void updateVialCounts(Container container, User user, List vials) ColumnInfo vialColumn = tableInfoVial.getColumn(entry.getKey()); if (null == vialColumn) throw new IllegalStateException("Expected Vial table column to exist."); - String fromName = vialColumn.getSelectName(); + var fromName = vialColumn.getSelectIdentifier(); for (RollupInstance rollupItem : entry.getValue()) { VialSpecimenRollup rollup = rollupItem.second; ColumnInfo column = tableInfoSpecimen.getColumn(rollupItem.first); if (null == column) throw new IllegalStateException("Expected Specimen table column to exist."); - String toName = column.getSelectName(); + var toName = column.getSelectIdentifier(); - updateSql.append(",\n\t\t").append(rollup.getRollupSql(fromName, toName)); + // TODO convert to SQLFragment + updateSql.append(",\n\t\t").append(rollup.getRollupSql(fromName.getId(), toName.getId())); } } @@ -943,22 +944,24 @@ public Map> load(@NotNull Container c, @Nullable Obj TableInfo tableInfo = schema.getTable(SpecimenQuerySchema.SPECIMEN_WRAP_TABLE_NAME); Map columnMap = queryService.getColumns(tableInfo, fieldKeys); - SQLFragment sql = queryService.getSelectSQL(tableInfo, columnMap.values(), null, null, -1, 0, false); + SQLFragment inner = queryService.getSelectSQL(tableInfo, columnMap.values(), null, null, -1, 0, false); // Insert COUNT String sampleCountName = tableInfo.getSqlDialect().makeLegalIdentifier("SampleCount"); - String countStr = " COUNT(*) As " + sampleCountName + ",\n"; - int insertIndex = sql.indexOf("SELECT"); - sql.insert(insertIndex + 6, countStr); + SQLFragment sql = new SQLFragment("SELECT COUNT(*) As " + sampleCountName); + for (var col : columnMap.values()) + sql.append(",").appendIdentifier(col.getAlias()); - sql.append("GROUP BY "); + sql.append("\nFROM (").append(inner).append(") AS details"); + + sql.append("\nGROUP BY "); boolean firstGroupBy = true; for (ColumnInfo columnInfo : columnMap.values()) { if (!firstGroupBy) sql.append(", "); firstGroupBy = false; - sql.append(columnInfo.getValueSql(tableInfo.getTitle())); + sql.appendIdentifier(columnInfo.getAlias()); } sql.append("\nORDER BY "); @@ -968,7 +971,7 @@ public Map> load(@NotNull Container c, @Nullable Obj if (!firstOrderBy) sql.append(", "); firstOrderBy = false; - sql.append(columnInfo.getValueSql(tableInfo.getTitle())); + sql.appendIdentifier(columnInfo.getAlias()); } SqlSelector selector = new SqlSelector(tableInfo.getSchema(), sql); @@ -1000,7 +1003,7 @@ else if (countObject instanceof Integer) GroupedValueColumnHelper columnHelper = getGroupedValueAllowedMap().get(s); ColumnInfo columnInfo = columnMap.get(columnHelper.getFieldKey()); - Object value = rowMap.get(columnInfo.getAlias()); + Object value = columnInfo.getValue(rowMap); String labelValue = (null != value) ? value.toString() : null; GroupedResults groupedResults = currentGroupedResultsMap.get(labelValue); if (null == groupedResults) diff --git a/specimen/src/org/labkey/specimen/actions/SpecimenController.java b/specimen/src/org/labkey/specimen/actions/SpecimenController.java index 52650107..10b23eca 100644 --- a/specimen/src/org/labkey/specimen/actions/SpecimenController.java +++ b/specimen/src/org/labkey/specimen/actions/SpecimenController.java @@ -5747,29 +5747,4 @@ public void addNavTrail(NavTree root) root.addChild("Insert " + _form.getQueryName()); } } -/* - // Used for testing - @RequiresSiteAdmin - public class DropVialIndices extends SimpleRedirectAction - { - @Override - public URLHelper getRedirectURL(Object o) throws Exception - { - new SpecimenTablesProvider(getContainer(), getUser(), null).dropTableIndices(SpecimenTablesProvider.VIAL_TABLENAME); - return new ActionURL(BeginAction.class, getContainer()); - } - } - - // Used for testing - @RequiresSiteAdmin - public class AddVialIndices extends SimpleRedirectAction - { - @Override - public URLHelper getRedirectURL(Object o) throws Exception - { - new SpecimenTablesProvider(getContainer(), getUser(), null).addTableIndices(SpecimenTablesProvider.VIAL_TABLENAME); - return new ActionURL(BeginAction.class, getContainer()); - } - } -*/ } diff --git a/specimen/src/org/labkey/specimen/importer/ImportableColumn.java b/specimen/src/org/labkey/specimen/importer/ImportableColumn.java index eb51de36..c556a687 100644 --- a/specimen/src/org/labkey/specimen/importer/ImportableColumn.java +++ b/specimen/src/org/labkey/specimen/importer/ImportableColumn.java @@ -1,6 +1,7 @@ package org.labkey.specimen.importer; import org.jetbrains.annotations.Nullable; +import org.labkey.api.data.DatabaseIdentifier; import org.labkey.api.data.JdbcType; import org.labkey.api.data.dialect.SqlDialect; import org.labkey.api.exp.PropertyDescriptor; @@ -122,9 +123,9 @@ public String getDbColumnName() return _dbColumnName; } - private String _legalDbColumnName; + private DatabaseIdentifier _legalDbColumnName; - public String getLegalDbColumnName(SqlDialect dialect) + public DatabaseIdentifier getLegalDbColumnName(SqlDialect dialect) { if (null == _legalDbColumnName) _legalDbColumnName = PropertyDescriptor.getLegalSelectNameFromStorageName(dialect, getDbColumnName()); diff --git a/specimen/src/org/labkey/specimen/importer/SpecimenImporter.java b/specimen/src/org/labkey/specimen/importer/SpecimenImporter.java index a4c52355..ac5e359d 100644 --- a/specimen/src/org/labkey/specimen/importer/SpecimenImporter.java +++ b/specimen/src/org/labkey/specimen/importer/SpecimenImporter.java @@ -30,6 +30,7 @@ import org.labkey.api.data.BaseColumnInfo; import org.labkey.api.data.ColumnInfo; import org.labkey.api.data.Container; +import org.labkey.api.data.DatabaseIdentifier; import org.labkey.api.data.DbSchema; import org.labkey.api.data.DbScope; import org.labkey.api.data.DbSequence; @@ -230,9 +231,9 @@ public DbSchema getSchema() private List _specimenCols; private List _vialCols; private List _vialEventCols; - private String _specimenColsSql; - private String _vialColsSql; - private String _vialEventColsSql; + private SQLFragment _specimenColsSql; + private SQLFragment _vialColsSql; + private SQLFragment _vialEventColsSql; private Logger _logger; private PipelineJob _job; @@ -815,7 +816,7 @@ private void updateCalculatedSpecimenData(final boolean merge, final boolean edi final Map siteMap = new HashMap<>(); TableInfo vialTable = getTableInfoVial(); - StringBuilder vialPropertiesSB = new StringBuilder("UPDATE ").append(vialTable.getSelectName()) + SQLFragment vialPropertiesSql = new SQLFragment("UPDATE ").append(vialTable.getSelectName()) .append(" SET CurrentLocation = CAST(? AS INTEGER), ProcessingLocation = CAST(? AS INTEGER), FirstProcessedByInitials = ?, AtRepository = ?, LatestComments = ?, LatestQualityComments = ? "); for (List> rollupList : getEventToVialRollups().values()) @@ -826,14 +827,12 @@ private void updateCalculatedSpecimenData(final boolean merge, final boolean edi ColumnInfo column = vialTable.getColumn(colName); if (null == column) throw new IllegalStateException("Expected Vial table column to exist."); - vialPropertiesSB.append(", ").append(column.getSelectName()).append(" = ") + vialPropertiesSql.append(", ").appendIdentifier(column.getSelectIdentifier()).append(" = ") .append(JdbcType.VARCHAR.equals(column.getJdbcType()) ? "?" : "CAST(? AS " + vialTable.getSqlDialect().getSqlCastTypeName(column.getJdbcType()) + ")"); } } - vialPropertiesSB.append(" WHERE RowId = ?"); - - final String vialPropertiesSql = vialPropertiesSB.toString(); + vialPropertiesSql.append(" WHERE RowId = ?"); _iTimer.setPhase(ImportPhases.HandleComments); @@ -966,11 +965,11 @@ private void updateCalculatedSpecimenData(final boolean merge, final boolean edi ColumnInfo column = getTableInfoSpecimenEvent().getColumn(eventColName); if (null == column) throw new IllegalStateException("Expected Specimen Event table column to exist."); - String eventColSelectName = column.getSelectName(); + var eventColSelectName = column.getSelectIdentifier(); for (RollupInstance rollupItem : rollupEntry.getValue()) { String vialColName = rollupItem.first; - Object rollupResult = rollupItem.second.getRollupResult(dateOrderedEvents, eventColSelectName, + Object rollupResult = rollupItem.second.getRollupResult(dateOrderedEvents, eventColSelectName.getId(), rollupItem.getFromType(), rollupItem.getToType()); if (!Objects.equals(vial.get(vialColName), rollupResult)) { @@ -1000,10 +999,10 @@ private void updateCalculatedSpecimenData(final boolean merge, final boolean edi ColumnInfo column = getTableInfoSpecimenEvent().getColumn(eventColName); if (null == column) throw new IllegalStateException("Expected Specimen Event table column to exist."); - String eventColAlias = column.getAlias(); // Use alias since we're looking up in the rowMap + var eventColAlias = column.getAlias(); // Use alias since we're looking up in the rowMap for (RollupInstance rollupItem : rollupEntry.getValue()) { - Object rollupResult = rollupItem.second.getRollupResult(dateOrderedEvents, eventColAlias, + Object rollupResult = rollupItem.second.getRollupResult(dateOrderedEvents, eventColAlias.getId(), rollupItem.getFromType(), rollupItem.getToType()); params.add(rollupResult); } @@ -1051,7 +1050,10 @@ private void updateCalculatedSpecimenData(final boolean merge, final boolean edi _iTimer.setPhase(ImportPhases.UpdateVials); if (!vialPropertiesParams.isEmpty()) - Table.batchExecute(SpecimenSchema.get().getSchema(), vialPropertiesSql, vialPropertiesParams); + { + assert vialPropertiesSql.getParams().isEmpty(); + Table.batchExecute(SpecimenSchema.get().getSchema(), vialPropertiesSql.getRawSQL(), vialPropertiesParams); + } _iTimer.setPhase(ImportPhases.UpdateComments); if (!commentParams.isEmpty()) @@ -1066,9 +1068,6 @@ private void updateCalculatedSpecimenData(final boolean merge, final boolean edi throw new RuntimeSQLException(e); } -// if (!merge) -// new SpecimenTablesProvider(getContainer(), getUser(), null).addTableIndices(SpecimenTablesProvider.VIAL_TABLENAME); - // finally, after all other data has been updated, we can update our cached specimen counts and processing locations: setStatus(GENERAL_JOB_STATUS_MSG + " (update counts)"); _iTimer.setPhase(ImportPhases.UpdateSpecimenProcessingInfo); @@ -1174,20 +1173,20 @@ private List getSpecimenCols(List availableColum return _specimenCols; } - private String getSpecimenColsSql(List availableColumns, boolean seenVisitValue) + private SQLFragment getSpecimenColsSql(List availableColumns, boolean seenVisitValue) { if (_specimenColsSql == null) { String sep = ""; - StringBuilder cols = new StringBuilder(); + SQLFragment cols = new SQLFragment(); for (SpecimenColumn col : getSpecimenCols(availableColumns)) { - cols.append(sep).append(col.getLegalDbColumnName(getSqlDialect())); + cols.append(sep).appendIdentifier(col.getLegalDbColumnName(getSqlDialect())); sep = ",\n "; } if (!seenVisitValue) cols.append(sep).append(VISIT_VALUE.getDbColumnName()); - _specimenColsSql = cols.toString(); + _specimenColsSql = cols; } return _specimenColsSql; } @@ -1207,18 +1206,18 @@ private List getVialCols(List availableColumns) return _vialCols; } - private String getVialColsSql(List availableColumns) + private SQLFragment getVialColsSql(List availableColumns) { if (_vialColsSql == null) { String sep = ""; - StringBuilder cols = new StringBuilder(); + SQLFragment cols = new SQLFragment(); for (SpecimenColumn col : getVialCols(availableColumns)) { cols.append(sep).append(col.getLegalDbColumnName(getSqlDialect())); sep = ",\n "; } - _vialColsSql = cols.toString(); + _vialColsSql = cols; } return _vialColsSql; } @@ -1238,18 +1237,18 @@ private List getSpecimenEventCols(List available return _vialEventCols; } - private String getSpecimenEventColsSql(List availableColumns) + private SQLFragment getSpecimenEventColsSql(List availableColumns) { if (_vialEventColsSql == null) { String sep = ""; - StringBuilder cols = new StringBuilder(); + SQLFragment cols = new SQLFragment(); for (SpecimenColumn col : getSpecimenEventCols(availableColumns)) { cols.append(sep).append(col.getLegalDbColumnName(getSqlDialect())); sep = ",\n "; } - _vialEventColsSql = cols.toString(); + _vialEventColsSql = cols; } return _vialEventColsSql; } @@ -1384,17 +1383,17 @@ private void populateMaterials(SpecimenLoadInfo info, boolean merge) info("exp.Material: Update complete."); } - private String getSpecimenEventTempTableColumns(SpecimenLoadInfo info) + private SQLFragment getSpecimenEventTempTableColumns(SpecimenLoadInfo info) { - StringBuilder columnList = new StringBuilder(); + SQLFragment columnList = new SQLFragment(); String prefix = ""; for (SpecimenColumn col : getSpecimenEventCols(info.getAvailableColumns())) { columnList.append(prefix); prefix = ", "; - columnList.append("\n ").append(info.getTempTableName()).append(".").append(col.getLegalDbColumnName(getSqlDialect())); + columnList.append("\n ").append(info.getTempTableName()).append(".").appendIdentifier(col.getLegalDbColumnName(getSqlDialect())); } - return columnList.toString(); + return columnList; } // NOTE: In merge case, we've already checked the specimen hash columns are not in conflict. @@ -1402,7 +1401,7 @@ private void appendConflictResolvingSQL(SqlDialect dialect, SQLFragment sql, Spe @Nullable SpecimenColumn castColumn) { // If castColumn no null, then we still count col, but then cast col's value to castColumn's type and name it castColumn's name - String selectCol = tempTableName + "." + col.getLegalDbColumnName(getSqlDialect()); + SQLFragment selectCol = new SQLFragment(tempTableName).append(".").appendIdentifier(col.getLegalDbColumnName(getSqlDialect())); if (col.getAggregateEventFunction() != null) { @@ -2024,7 +2023,15 @@ public void setCurrentRow(int currentRow) pump.run(); if (dix.getErrors().hasErrors()) { - throw new ValidationException(dix.getErrors().getLastRowError().getMessage() + " (File: " + file.getTableType().getName() + ")"); + var last = dix.getErrors().getLastRowError(); + var vex = new ValidationException(last.getMessage() + " (File: " + file.getTableType().getName() + ")") + { + @Override + public synchronized Throwable getCause() + { + return last; + } + }; } rowCount = pump.getRowCount(); @@ -2163,13 +2170,13 @@ private class SpecimenImportIterator extends SimpleTranslator for (final ImportableColumn ic : sib.importColumns) { - if (seen.add(ic.getLegalDbColumnName(d))) + if (seen.add(ic.getLegalDbColumnName(d).getId())) { String boundInputColumnName = null; if (tsvColumnNames.contains(ic.getPrimaryTsvColumnName())) boundInputColumnName = ic.getPrimaryTsvColumnName(); final String name = boundInputColumnName; - ColumnInfo col = new BaseColumnInfo(ic.getLegalDbColumnName(d), ic.getJdbcType()); + ColumnInfo col = new BaseColumnInfo(ic.getLegalDbColumnName(d).getId(), ic.getJdbcType()); Supplier call = () -> { Object ret = null; if (null != name) @@ -2451,13 +2458,13 @@ protected void checkForConflictingSpecimens(DbSchema schema, String tempTable, L info("Checking for conflicting specimens before merging..."); // Columns used in the specimen hash - StringBuilder hashCols = new StringBuilder(); + SQLFragment hashCols = new SQLFragment(); for (SpecimenColumn col : loadedColumns) { if (col.getTargetTable().isSpecimens() && col.getAggregateEventFunction() == null) { hashCols.append(",\n\t"); - hashCols.append(col.getLegalDbColumnName(getSqlDialect())); + hashCols.appendIdentifier(col.getLegalDbColumnName(getSqlDialect())); } } hashCols.append("\n"); @@ -2584,22 +2591,22 @@ private void updateTempTableSpecimenHash(TempTablesHolder tempTablesHolder, List if (col.getTargetTable().isSpecimens()) { conflictResolvingSubselect.append(",\n\t"); - String selectCol = tempTableName + "." + col.getLegalDbColumnName(getSqlDialect()); + SQLFragment selectCol = new SQLFragment(tempTableName).append(".").appendIdentifier(col.getLegalDbColumnName(getSqlDialect())); if (col.getAggregateEventFunction() != null) conflictResolvingSubselect.append(col.getAggregateEventFunction()).append("(").append(selectCol).append(")"); else { - String singletonAggregate; + SQLFragment singletonAggregate; if (col.getJavaClass().equals(Boolean.class)) { // gross nested calls to cast the boolean to an int, get its min, then cast back to a boolean. // this is needed because most aggregates don't work on boolean values. - singletonAggregate = "CAST(MIN(CAST(" + selectCol + " AS INTEGER)) AS " + schema.getSqlDialect().getBooleanDataType() + ")"; + singletonAggregate = new SQLFragment("CAST(MIN(CAST(").append(selectCol).append(" AS INTEGER)) AS ").append(schema.getSqlDialect().getBooleanDataType()).append(")"); } else { - singletonAggregate = "MIN(" + selectCol + ")"; + singletonAggregate = new SQLFragment("MIN(").append(selectCol).append(")"); } conflictResolvingSubselect.append("CASE WHEN"); conflictResolvingSubselect.append(" COUNT(DISTINCT ").append(selectCol).append(") = 1 THEN "); @@ -2612,7 +2619,7 @@ private void updateTempTableSpecimenHash(TempTablesHolder tempTablesHolder, List conflictResolvingSubselect.append("\nFROM ").append(tempTableName).append("\nGROUP BY GlobalUniqueId"); SQLFragment updateHashSql = new SQLFragment("SELECT ("); - makeUpdateSpecimenHashSql(schema, getContainer(), loadedColumns, "InnerTable.", updateHashSql); + makeUpdateSpecimenHashSql(schema, getContainer(), loadedColumns, "InnerTable", updateHashSql); updateHashSql.append(") AS SpecimenHash, ") .append("InnerTable.GlobalUniqueId") .append("\n\tINTO ") @@ -2655,15 +2662,11 @@ public static void makeUpdateSpecimenHashSql(DbSchema schema, Container containe SpecimenColumns.BASE_SPECIMEN_COLUMNS.forEach(col -> { if (col.getTargetTable().isSpecimens()) { + hash.add(new SQLFragment("'~'")); if (loadedColumnMap.isEmpty() || loadedColumnMap.containsKey(col.getPrimaryTsvColumnName())) { - String columnName = innerTable + col.getLegalDbColumnName(schema.getSqlDialect()); - hash.add(new SQLFragment("'~'")); - hash.add(new SQLFragment(" CASE WHEN " + columnName + " IS NOT NULL THEN CAST(" + columnName + " AS " + strType + ") ELSE '' END")); - } - else - { - hash.add(new SQLFragment("'~'")); + SQLFragment columnNameFragment = new SQLFragment().appendIdentifier(innerTable).append(".").appendIdentifier(col.getLegalDbColumnName(schema.getSqlDialect())); + hash.add(new SQLFragment(" COALESCE(CAST(").append(columnNameFragment).append(" AS ").append(strType).append("),'')")); } } }); @@ -2715,7 +2718,7 @@ private TempTablesHolder createTempTable() info("Creating temp table to hold archive data..."); SqlDialect dialect = DbSchema.getTemp().getSqlDialect(); - StringBuilder sql = new StringBuilder(); + SQLFragment sql = new SQLFragment(); String uniquifier = StringUtilsLabKey.getUniquifier(9); ArrayList columns = new ArrayList<>(); @@ -2740,9 +2743,9 @@ private TempTablesHolder createTempTable() for (SpecimenColumn col : _specimenColumns) { - String name = col.getLegalDbColumnName(getSqlDialect()); - sql.append(",\n ").append(name).append(" ").append(col.getDbType()); - BaseColumnInfo colInfo = new BaseColumnInfo(name, col.getJdbcType(), col.getMaxSize(), true); + DatabaseIdentifier name = col.getLegalDbColumnName(getSqlDialect()); + sql.append(",\n ").appendIdentifier(name).append(" ").append(col.getDbType()); + BaseColumnInfo colInfo = new BaseColumnInfo(name.getId(), col.getJdbcType(), col.getMaxSize(), true); Collection aliases = col.getImportAliases(); if (!aliases.isEmpty()) colInfo.setImportAliasesSet(new HashSet<>(aliases)); diff --git a/specimen/src/org/labkey/specimen/query/SpecimenQueryView.java b/specimen/src/org/labkey/specimen/query/SpecimenQueryView.java index 93b3e6f1..3198ca88 100644 --- a/specimen/src/org/labkey/specimen/query/SpecimenQueryView.java +++ b/specimen/src/org/labkey/specimen/query/SpecimenQueryView.java @@ -140,17 +140,17 @@ protected List getRequiredColumns() return cols; } - protected String getRequiredColumnAlias(String columnName) + protected ColumnInfo getRequiredColumn(String columnName) { ColumnInfo col = _requiredColumns.get(columnName); if (col == null) throw new IllegalStateException("Failed to find expected column " + columnName); - return col.getAlias(); + return col; } protected Object getRequiredColumnValue(RenderContext ctx, String columnName) { - return ctx.getRow().get(getRequiredColumnAlias(columnName)); + return getRequiredColumn(columnName).getValue(ctx); } } @@ -183,8 +183,8 @@ protected void renderExtraRecordSelectorContent(RenderContext ctx, HtmlWriter ou { if (!isAvailable(ctx)) { - String reasonAlias = getRequiredColumnAlias("AvailabilityReason"); - Object reason = ctx.getRow().get(reasonAlias); + var reasonCol = getRequiredColumn("AvailabilityReason"); + Object reason = reasonCol.getValue(ctx.getRow()); HtmlStringBuilder builder = HtmlStringBuilder.of(reason instanceof String r ? r : "Specimen Unavailable.") .append(HtmlString.BR).append(HtmlString.BR) @@ -207,7 +207,7 @@ private ActionURL getHistoryLink(RenderContext ctx) private boolean isAvailable(RenderContext ctx) { - return StudyUtils.isFieldTrue(ctx, getRequiredColumnAlias("Available")); + return StudyUtils.isFieldTrue(ctx, getRequiredColumn("Available")); } } diff --git a/specimen/src/org/labkey/specimen/report/SpecimenReportManager.java b/specimen/src/org/labkey/specimen/report/SpecimenReportManager.java index c23d6bbd..bc48878f 100644 --- a/specimen/src/org/labkey/specimen/report/SpecimenReportManager.java +++ b/specimen/src/org/labkey/specimen/report/SpecimenReportManager.java @@ -103,9 +103,9 @@ private SpecimenDetailQueryHelper getSpecimenDetailQueryHelper(Container contain for (SpecimenTypeBeanProperty typeProperty : level.getGroupingColumns()) { ColumnInfo col = colMap.get(typeProperty.getTypeKey()); - builder.append(sep).append(col.getAlias()); + builder.append(sep).append(col.getAlias().getId()); sep = ", "; - aliasToTypeProperty.put(col.getAlias(), typeProperty); + aliasToTypeProperty.put(col.getAlias().getId(), typeProperty); } groupingColSql = builder.toString(); } diff --git a/specimen/src/org/labkey/specimen/report/SpecimenVisitReportParameters.java b/specimen/src/org/labkey/specimen/report/SpecimenVisitReportParameters.java index fea8b56d..882a5e07 100644 --- a/specimen/src/org/labkey/specimen/report/SpecimenVisitReportParameters.java +++ b/specimen/src/org/labkey/specimen/report/SpecimenVisitReportParameters.java @@ -15,6 +15,7 @@ */ package org.labkey.specimen.report; +import org.labkey.api.data.SQLFragment; import org.labkey.api.data.SimpleFilter; import org.labkey.api.jsp.taglib.AutoCompleteTextTag; import org.labkey.api.query.CustomView; @@ -288,10 +289,10 @@ protected void addParticipantGroupFilter(SimpleFilter filter, int ptidListId) ParticipantGroup group = ParticipantGroupService.get().getParticipantGroup(getContainer(), getUser(), ptidListId); if (group != null) { - StringBuilder sql = new StringBuilder(); + SQLFragment sql = new SQLFragment(); sql.append("(").append(StudyService.get().getSubjectColumnName(getContainer())).append(" IN (SELECT "); - sql.append("ParticipantId FROM ").append(SpecimenSchema.get().getTableInfoParticipantGroupMap()).append(" WHERE GroupId = ?))"); - filter.addWhereClause(sql.toString(), new Object[] { ptidListId }); + sql.append("ParticipantId FROM ").append(SpecimenSchema.get().getTableInfoParticipantGroupMap()).append(" WHERE GroupId = ?))").add(ptidListId); + filter.addWhereClause(sql); } } diff --git a/specimen/src/org/labkey/specimen/writer/SpecimenWriter.java b/specimen/src/org/labkey/specimen/writer/SpecimenWriter.java index 9265e940..cee15fe0 100644 --- a/specimen/src/org/labkey/specimen/writer/SpecimenWriter.java +++ b/specimen/src/org/labkey/specimen/writer/SpecimenWriter.java @@ -131,17 +131,17 @@ else if (null == column.getFkColumn()) { col = new SQLFragment("NULL"); } - col.append(" AS ").append(dc.getDisplayColumnInfo().getAlias()); + col.append(" AS ").appendIdentifier(dc.getDisplayColumnInfo().getAlias()); } else { // DisplayColumn will use getAlias() to retrieve the value from the map - col = new SQLFragment(column.getFkTableAlias() + "." + column.getFkColumn() + " AS " + dc.getDisplayColumn().getAlias()); + col = new SQLFragment(column.getFkTableAlias() + "." + column.getFkColumn() + " AS ").appendIdentifier(dc.getDisplayColumn().getAlias()); // Don't export values for columns set at or above the PHI export level if (shouldRemovePhi(ctx.getPhiLevel(), column, queryColumn)) { - col = new SQLFragment("NULL AS " + dc.getDisplayColumn().getAlias()); + col = new SQLFragment("NULL AS ").appendIdentifier(dc.getDisplayColumn().getAlias()); } } @@ -167,7 +167,7 @@ else if (null == column.getFkColumn()) sql.append(column.getJoinType()).append(" "); sql.append("JOIN ").append(specimenTableManager.getTableInfoFromFkTableName(column.getFkTable())).append(" AS ").append(column.getFkTableAlias()).append(" ON "); sql.append("(se."); - sql.append(ci.getSelectName()).append(" = ").append(column.getFkTableAlias()).append(".RowId)"); + sql.appendIdentifier(ci.getSelectIdentifier()).append(" = ").append(column.getFkTableAlias()).append(".RowId)"); } } @@ -227,9 +227,9 @@ private ColumnInfo getSpecimenQueryColumn(TableInfo queryTable, SpecimenColumn c if (null != queryTable.getColumn(column.getDbColumnName())) return queryTable.getColumn(column.getDbColumnName()); - String legalName = PropertyDescriptor.getLegalSelectNameFromStorageName(dialect, column.getDbColumnName()); - if (null != queryTable.getColumn(legalName)) - return queryTable.getColumn(legalName); + var legalName = PropertyDescriptor.getLegalSelectNameFromStorageName(dialect, column.getDbColumnName()); + if (null != queryTable.getColumn(legalName.getId())) + return queryTable.getColumn(legalName.getId()); if (column.getDbColumnName().toLowerCase().endsWith("id")) { From 154dbc9f0d4b37bc1702452511d6a84b88adfeb3 Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Mon, 2 Jun 2025 21:33:55 +0100 Subject: [PATCH 445/494] Migrate to HtmlWriter Original PR: https://github.com/LabKey/platform/pull/6682 --- .../SpecimenCommentAuditProvider.java | 12 +-- .../specimen/actions/ButtonBarLineBreak.java | 13 +-- .../specimen/actions/SpecimenController.java | 31 ++++--- .../query/SpecimenRequestDisplayColumn.java | 90 ++++++++++--------- .../query/SpecimenRequestQueryView.java | 12 +-- 5 files changed, 80 insertions(+), 78 deletions(-) diff --git a/specimen/src/org/labkey/specimen/SpecimenCommentAuditProvider.java b/specimen/src/org/labkey/specimen/SpecimenCommentAuditProvider.java index cc3becb4..d7ddb3a9 100644 --- a/specimen/src/org/labkey/specimen/SpecimenCommentAuditProvider.java +++ b/specimen/src/org/labkey/specimen/SpecimenCommentAuditProvider.java @@ -36,8 +36,6 @@ import org.labkey.api.writer.HtmlWriter; import org.labkey.specimen.actions.SpecimenController; -import java.io.IOException; -import java.io.Writer; import java.util.List; import java.util.Map; import java.util.Set; @@ -45,10 +43,6 @@ import static org.labkey.specimen.SpecimenCommentAuditDomainKind.COLUMN_NAME_VIAL_ID; import static org.labkey.specimen.SpecimenCommentAuditDomainKind.SPECIMEN_COMMENT_EVENT; -/** - * User: klum - * Date: 7/18/13 - */ public class SpecimenCommentAuditProvider extends AbstractAuditTypeProvider implements AuditTypeProvider { private static final List defaultVisibleColumns = List.of( @@ -126,7 +120,7 @@ public void addQueryColumns(Set columns) } @Override - public void renderGridCellContents(RenderContext ctx, Writer oldWriter, HtmlWriter out) throws IOException + public void renderGridCellContents(RenderContext ctx, HtmlWriter out) { Object containerId = containerColumn.getValue(ctx); String globalUniqueId = (String) getValue(ctx); @@ -136,12 +130,12 @@ public void renderGridCellContents(RenderContext ctx, Writer oldWriter, HtmlWrit Container container = ContainerManager.getForId(containerId.toString()); if (container == null) { - oldWriter.write(globalUniqueId); + out.write(globalUniqueId); return; } ActionURL url = SpecimenController.getCommentURL(container, globalUniqueId); - oldWriter.write(LinkBuilder.simpleLink(globalUniqueId, url).toString()); + out.write(LinkBuilder.simpleLink(globalUniqueId, url)); } }; } diff --git a/specimen/src/org/labkey/specimen/actions/ButtonBarLineBreak.java b/specimen/src/org/labkey/specimen/actions/ButtonBarLineBreak.java index 91440493..51a1c2a2 100644 --- a/specimen/src/org/labkey/specimen/actions/ButtonBarLineBreak.java +++ b/specimen/src/org/labkey/specimen/actions/ButtonBarLineBreak.java @@ -17,20 +17,15 @@ package org.labkey.specimen.actions; import org.labkey.api.data.RenderContext; +import org.labkey.api.util.HtmlString; import org.labkey.api.view.DisplayElement; +import org.labkey.api.writer.HtmlWriter; -import java.io.IOException; -import java.io.Writer; - -/** - * User: jeckels - * Date: Nov 7, 2006 - */ public class ButtonBarLineBreak extends DisplayElement { @Override - public void render(RenderContext ctx, Writer out) throws IOException + public void render(RenderContext ctx, HtmlWriter out) { - out.write("
"); + out.write(HtmlString.BR); } } diff --git a/specimen/src/org/labkey/specimen/actions/SpecimenController.java b/specimen/src/org/labkey/specimen/actions/SpecimenController.java index 10b23eca..d17a6e60 100644 --- a/specimen/src/org/labkey/specimen/actions/SpecimenController.java +++ b/specimen/src/org/labkey/specimen/actions/SpecimenController.java @@ -115,13 +115,14 @@ import org.labkey.api.util.ExceptionUtil; import org.labkey.api.util.FileStream; import org.labkey.api.util.GUID; +import org.labkey.api.util.HtmlString; import org.labkey.api.util.LinkBuilder; import org.labkey.api.util.MailHelper; +import org.labkey.api.util.OptionBuilder; import org.labkey.api.util.PageFlowUtil; import org.labkey.api.util.Pair; -import org.labkey.api.util.URLHelper; -import org.labkey.api.util.OptionBuilder; import org.labkey.api.util.SelectBuilder; +import org.labkey.api.util.URLHelper; import org.labkey.api.util.emailTemplate.EmailTemplateService; import org.labkey.api.view.ActionURL; import org.labkey.api.view.AjaxCompletion; @@ -194,7 +195,6 @@ import java.io.File; import java.io.IOException; import java.io.OutputStream; -import java.io.Writer; import java.nio.charset.StandardCharsets; import java.sql.ResultSet; import java.sql.SQLException; @@ -211,6 +211,12 @@ import java.util.Map; import java.util.Set; +import static org.labkey.api.util.DOM.Attribute.src; +import static org.labkey.api.util.DOM.Attribute.style; +import static org.labkey.api.util.DOM.IMG; +import static org.labkey.api.util.DOM.SPAN; +import static org.labkey.api.util.DOM.at; + public class SpecimenController extends SpringActionController { private static final Logger LOG = LogManager.getLogger(SpecimenController.class); @@ -551,7 +557,7 @@ public SpecimenQueryView getSpecimenQueryView(boolean showVials, boolean forExpo public static final class ExcludeSiteDropDown extends DisplayElement { @Override - public void render(RenderContext ctx, Writer out) throws IOException + public void render(RenderContext ctx, HtmlWriter out) { ActionURL url = ctx.getViewContext().cloneActionURL(); url.deleteParameter(SpecimenQueryView.PARAMS.excludeRequestedBySite.name()); @@ -3310,7 +3316,7 @@ public AttachmentDisplayColumn(HttpServletRequest request) } @Override - public void renderGridCellContents(RenderContext ctx, Writer oldWriter, HtmlWriter out) throws IOException + public void renderGridCellContents(RenderContext ctx, HtmlWriter out) { Map cols = ctx.getRow(); SpecimenRequestEvent event = ObjectFactory.Registry.getFactory(SpecimenRequestEvent.class).fromMap(cols); @@ -3320,14 +3326,19 @@ public void renderGridCellContents(RenderContext ctx, Writer oldWriter, HtmlWrit { for (Attachment attachment : attachments) { - oldWriter.write(""); - oldWriter.write(""); - oldWriter.write(PageFlowUtil.filter(attachment.getName())); - oldWriter.write("
"); + out.write(LinkBuilder.simpleLink( + SPAN( + IMG(at(style,"padding-right:4pt;", src, _request.getContextPath() + attachment.getFileIcon())), + attachment.getName() + )) + .href(getDownloadURL(event, attachment.getName()))); + out.write(HtmlString.BR); } } else - oldWriter.write(" "); + { + out.write(HtmlString.NBSP); + } } } diff --git a/specimen/src/org/labkey/specimen/query/SpecimenRequestDisplayColumn.java b/specimen/src/org/labkey/specimen/query/SpecimenRequestDisplayColumn.java index 87768917..b68c7d0c 100644 --- a/specimen/src/org/labkey/specimen/query/SpecimenRequestDisplayColumn.java +++ b/specimen/src/org/labkey/specimen/query/SpecimenRequestDisplayColumn.java @@ -20,14 +20,23 @@ import org.labkey.api.data.SimpleDisplayColumn; import org.labkey.api.data.TableInfo; import org.labkey.api.study.StudyUtils; +import org.labkey.api.util.DOM; +import org.labkey.api.util.DOM.Renderable; import org.labkey.api.util.HtmlString; import org.labkey.api.util.PageFlowUtil; +import org.labkey.api.util.PageFlowUtil.HelpPopupBuilder; import org.labkey.api.writer.HtmlWriter; -import java.io.IOException; -import java.io.Writer; import java.util.Set; +import static org.labkey.api.util.DOM.Attribute.id; +import static org.labkey.api.util.DOM.Attribute.src; +import static org.labkey.api.util.DOM.Attribute.style; +import static org.labkey.api.util.DOM.DIV; +import static org.labkey.api.util.DOM.IMG; +import static org.labkey.api.util.DOM.SPAN; +import static org.labkey.api.util.DOM.at; + public class SpecimenRequestDisplayColumn extends SimpleDisplayColumn { private final boolean _showOneVialIndicator; @@ -47,65 +56,66 @@ public SpecimenRequestDisplayColumn(SpecimenQueryView specimenQueryView, TableIn } @Override - public void renderGridCellContents(RenderContext ctx, Writer oldWriter, HtmlWriter out) throws IOException + public void renderGridCellContents(RenderContext ctx, HtmlWriter out) { String hash = (String) ctx.getRow().get("SpecimenHash"); String globalUniqueId = (String) ctx.getRow().get("GlobalUniqueId"); - int count = -1; + int c = -1; if (ctx.getRow().get("AvailableCount") != null) { - count = ((Number)ctx.getRow().get("AvailableCount")).intValue(); + c = ((Number)ctx.getRow().get("AvailableCount")).intValue(); } else { - Integer c = _specimenQueryView.getSampleCounts(ctx).get(hash); - if (c != null) - count = c.intValue(); + Integer sampleCount = _specimenQueryView.getSampleCounts(ctx).get(hash); + if (sampleCount != null) + c = sampleCount.intValue(); } + int count = c; boolean vialView = _specimenQueryView.isShowingVials(); boolean available = (!vialView && count > 0) || (vialView && StudyUtils.isFieldTrue(ctx, "Available")); boolean showCart = _showCartLinks && available; - String script = null; - oldWriter.write("
"); - if (showCart) - { - script = globalUniqueId == null ? "requestByHash('" + hash + "'); return false;" - : "requestByGlobalUniqueId('" + globalUniqueId + "'); return false;"; - oldWriter.write(""); - } - if (_showOneVialIndicator && count == 1) - { - oldWriter.write(getVialCountHtml(ctx, "", - "One Vial Available", "Only one vial of this primary specimen is available.", script)); - } - else if (_showZeroVialIndicator && count == 0) - { - oldWriter.write(getVialCountHtml(ctx, "", - "Zero Vials Available", "No vials of this primary specimen are currently available for request.", null)); - } - else - { - oldWriter.write(getVialCountHtml(ctx, "
" + String.valueOf(count) + "
", - count + " Vials Available", count + " vials of this primary specimen are currently available for new requests.", script)); - } + SPAN( + at(style,"text-align: center").at(showCart, id, (globalUniqueId != null ? globalUniqueId : hash)), + (Renderable) ret -> { + String script = null; + + if (showCart) + { + script = (globalUniqueId == null ? "requestByHash('" + hash + "'); return false;" + : "requestByGlobalUniqueId('" + globalUniqueId + "'); return false;"); + } - if (showCart) - oldWriter.write("
"); - oldWriter.write("
"); + if (_showOneVialIndicator && count == 1) + { + out.write(getVialCountHtml(ctx, IMG(at(src, ctx.getViewContext().getContextPath() + "/_images/one.png")), + "One Vial Available", HtmlString.of("Only one vial of this primary specimen is available."), script)); + } + else if (_showZeroVialIndicator && count == 0) + { + out.write(getVialCountHtml(ctx, IMG(at(src, ctx.getViewContext().getContextPath() + "/_images/zero.png")), + "Zero Vials Available", HtmlString.of("No vials of this primary specimen are currently available for request."), null)); + } + else + { + out.write(getVialCountHtml(ctx, DIV(at(style, "color:gray"), count), + count + " Vials Available", HtmlString.of(count + " vials of this primary specimen are currently available for new requests."), script)); + } + return ret; + } + ).appendTo(out); } - private String getVialCountHtml(RenderContext ctx, String cellHtml, String popupTitle, String popupBody, String requestScript) + private HelpPopupBuilder getVialCountHtml(RenderContext ctx, Renderable cellHtml, String popupTitle, Renderable popupBody, String requestScript) { - StringBuilder builder = new StringBuilder(); if (requestScript != null) { - cellHtml += ""; - popupBody += "

Click the shopping cart icon to request this specimen."; + cellHtml = DOM.createHtmlFragment(cellHtml, IMG(at(src, ctx.getViewContext().getContextPath() + "/_images/cart.png"))); + popupBody = DOM.createHtmlFragment(popupBody, HtmlString.BR, HtmlString.BR, "Click the shopping cart icon to request this specimen."); } - builder.append(PageFlowUtil.popupHelp(HtmlString.unsafe(popupBody), popupTitle).link(HtmlString.unsafe(cellHtml)).script(requestScript)); - return builder.toString(); + return PageFlowUtil.popupHelp(popupBody, popupTitle).link(cellHtml).script(requestScript); } @Override diff --git a/specimen/src/org/labkey/specimen/query/SpecimenRequestQueryView.java b/specimen/src/org/labkey/specimen/query/SpecimenRequestQueryView.java index 24fe07f8..65b6d38a 100644 --- a/specimen/src/org/labkey/specimen/query/SpecimenRequestQueryView.java +++ b/specimen/src/org/labkey/specimen/query/SpecimenRequestQueryView.java @@ -177,12 +177,6 @@ private static SimpleFilter addFilterClauses(@Nullable SimpleFilter filter) return filter; } - @Override - protected DataRegion.ButtonBarPosition getButtonBarPosition() - { - return DataRegion.ButtonBarPosition.TOP; - } - @Override protected DataRegion createDataRegion() { @@ -197,8 +191,8 @@ protected DataRegion createDataRegion() if (_showOptionLinks || (_extraLinks != null && _extraLinks.length > 0)) { RequestOptionDisplayColumn optionCol = - new RequestOptionDisplayColumn(getViewContext(), _showOptionLinks, getTable().getColumn("CreatedBy"), - getTable().getColumn("Status"), _extraLinks); + new RequestOptionDisplayColumn(getViewContext(), _showOptionLinks, getTable().getColumn("CreatedBy"), + getTable().getColumn("Status"), _extraLinks); rgn.addDisplayColumn(0, optionCol); } rgn.setSortable(_allowSortAndFilter); @@ -221,6 +215,4 @@ public void setShowCustomizeLink(boolean showCustomizeLink) { getSettings().setAllowCustomizeView(showCustomizeLink); } - - } From 18110fe5f1f27194215c0ed5d3dfa5fb9dd66a70 Mon Sep 17 00:00:00 2001 From: Susan Hert Date: Wed, 4 Jun 2025 07:36:13 -0700 Subject: [PATCH 446/494] Issue 52824: Don't mutate domains from the cache Original PR: https://github.com/LabKey/platform/pull/6690 --- specimen/src/org/labkey/specimen/SpecimenTableManager.java | 4 ++-- .../labkey/specimen/importer/SpecimenSchemaImporter.java | 2 +- specimen/src/org/labkey/specimen/view/manageSpecimens.jsp | 6 +++--- .../org/labkey/specimen/writer/SpecimenArchiveWriter.java | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/specimen/src/org/labkey/specimen/SpecimenTableManager.java b/specimen/src/org/labkey/specimen/SpecimenTableManager.java index 116caa3a..1e9f4af2 100644 --- a/specimen/src/org/labkey/specimen/SpecimenTableManager.java +++ b/specimen/src/org/labkey/specimen/SpecimenTableManager.java @@ -139,7 +139,7 @@ private Collection determineSpecimenColumns() SpecimenTablesProvider specimenTablesProvider = new SpecimenTablesProvider(_container, _user, null); - Domain vialDomain = specimenTablesProvider.getDomain("Vial", true); + Domain vialDomain = specimenTablesProvider.getDomain("Vial", true, false); if (null == vialDomain) throw new IllegalStateException("Expected Vial domain to already be created."); @@ -147,7 +147,7 @@ private Collection determineSpecimenColumns() for (DomainProperty domainProperty : vialDomain.getNonBaseProperties()) vialProperties.add(domainProperty.getPropertyDescriptor()); - Domain specimenEventDomain = specimenTablesProvider.getDomain("SpecimenEvent", true); + Domain specimenEventDomain = specimenTablesProvider.getDomain("SpecimenEvent", true, false); if (null == specimenEventDomain) throw new IllegalStateException("Expected SpecimenEvent domain to already be created."); diff --git a/specimen/src/org/labkey/specimen/importer/SpecimenSchemaImporter.java b/specimen/src/org/labkey/specimen/importer/SpecimenSchemaImporter.java index 3b497672..7ace1092 100644 --- a/specimen/src/org/labkey/specimen/importer/SpecimenSchemaImporter.java +++ b/specimen/src/org/labkey/specimen/importer/SpecimenSchemaImporter.java @@ -169,7 +169,7 @@ public void process(SimpleStudyImportContext ctx, VirtualFile studyDir, BindExce final String tableName = tableXml.getTableName(); // get the domain of the table we are updating - final Domain domain = tablesProvider.getDomain(tableName, false); + final Domain domain = tablesProvider.getDomain(tableName, false, true); final Container container = ctx.getContainer(); if (domain != null) diff --git a/specimen/src/org/labkey/specimen/view/manageSpecimens.jsp b/specimen/src/org/labkey/specimen/view/manageSpecimens.jsp index ba2b586f..3f221e66 100644 --- a/specimen/src/org/labkey/specimen/view/manageSpecimens.jsp +++ b/specimen/src/org/labkey/specimen/view/manageSpecimens.jsp @@ -56,9 +56,9 @@ if (!study.hasSourceStudy() && !study.isSnapshotStudy()) { SpecimenTablesProvider stp = new SpecimenTablesProvider(c, user, null); - Domain domainEvent = stp.getDomain("specimenevent",false); - Domain domainVial = stp.getDomain("vial",false); - Domain domainSpecimen = stp.getDomain("specimen",false); + Domain domainEvent = stp.getDomain("specimenevent",false, false); + Domain domainVial = stp.getDomain("vial",false, false); + Domain domainSpecimen = stp.getDomain("specimen",false, false); ActionURL specimenEventUrl = null; ActionURL vialUrl = null; diff --git a/specimen/src/org/labkey/specimen/writer/SpecimenArchiveWriter.java b/specimen/src/org/labkey/specimen/writer/SpecimenArchiveWriter.java index b6775986..398784a5 100644 --- a/specimen/src/org/labkey/specimen/writer/SpecimenArchiveWriter.java +++ b/specimen/src/org/labkey/specimen/writer/SpecimenArchiveWriter.java @@ -103,7 +103,7 @@ public void write(Study study, SimpleStudyExportContext ctx, VirtualFile root) t { TableType tableXml = tablesXml.addNewTable(); - Domain domain = tablesProvider.getDomain(entry.getKey(), false); + Domain domain = tablesProvider.getDomain(entry.getKey(), false, false); TableInfo table = entry.getValue(); List columns = new ArrayList<>(); From d60f971ecb1c63c549a7a863099bcec8c73c17d4 Mon Sep 17 00:00:00 2001 From: Josh Eckels Date: Wed, 11 Jun 2025 14:32:56 -0700 Subject: [PATCH 447/494] Minor auto-refactor code cleanup on a massive scale Original PR: https://github.com/LabKey/testAutomation/pull/2479 Co-authored-by: labkey-tchad --- src/org/labkey/test/tests/SpecimenCustomizeTest.java | 2 +- src/org/labkey/test/tests/SpecimenGridExportTest.java | 2 +- src/org/labkey/test/tests/SpecimenImportTest.java | 2 +- src/org/labkey/test/tests/SpecimenTest.java | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/org/labkey/test/tests/SpecimenCustomizeTest.java b/src/org/labkey/test/tests/SpecimenCustomizeTest.java index 8193706c..d01a2a27 100644 --- a/src/org/labkey/test/tests/SpecimenCustomizeTest.java +++ b/src/org/labkey/test/tests/SpecimenCustomizeTest.java @@ -261,7 +261,7 @@ public String getType() public Boolean isEditable() { - return _tr.findElements(By.xpath(".//div[contains(@id,'name')]/input")).size() > 0; + return !_tr.findElements(By.xpath(".//div[contains(@id,'name')]/input")).isEmpty(); } public void editName(String value) diff --git a/src/org/labkey/test/tests/SpecimenGridExportTest.java b/src/org/labkey/test/tests/SpecimenGridExportTest.java index f059b432..0ee1cf70 100644 --- a/src/org/labkey/test/tests/SpecimenGridExportTest.java +++ b/src/org/labkey/test/tests/SpecimenGridExportTest.java @@ -47,7 +47,7 @@ public class SpecimenGridExportTest extends AbstractExportTest @BeforeClass public static void doSetup() throws Exception { - SpecimenGridExportTest initTest = (SpecimenGridExportTest) getCurrentTest(); + SpecimenGridExportTest initTest = getCurrentTest(); initTest._containerHelper.createProject(initTest.getProjectName(), "Study"); initTest._containerHelper.enableModule("Specimen"); diff --git a/src/org/labkey/test/tests/SpecimenImportTest.java b/src/org/labkey/test/tests/SpecimenImportTest.java index dc4e6657..28f5d158 100644 --- a/src/org/labkey/test/tests/SpecimenImportTest.java +++ b/src/org/labkey/test/tests/SpecimenImportTest.java @@ -158,7 +158,7 @@ protected void assertSampleTypeData() List> sampleTypeData = getSampleDataFromDB(folderPath, "Study Specimens", fields); - Assert.assertNotEquals("There are no rows in the \"Study Specimens\" sample type.", sampleTypeData.size(), 0); + Assert.assertNotEquals("There are no rows in the \"Study Specimens\" sample type.", 0, sampleTypeData.size()); List expectedNames = Arrays.asList("1", "2", "3", "4"); diff --git a/src/org/labkey/test/tests/SpecimenTest.java b/src/org/labkey/test/tests/SpecimenTest.java index b7b186e7..0277fc6e 100644 --- a/src/org/labkey/test/tests/SpecimenTest.java +++ b/src/org/labkey/test/tests/SpecimenTest.java @@ -891,8 +891,8 @@ private enum StudyLocationType SAL("Site Affiliated Lab", 8), ENDPOINT("Endpoint Lab", 8); - private String _type; - private int _count; + private final String _type; + private final int _count; StudyLocationType(String type, int count) { From 8a68fd6949d6abd29a835d2f719885158f07da11 Mon Sep 17 00:00:00 2001 From: Josh Eckels Date: Wed, 11 Jun 2025 14:34:23 -0700 Subject: [PATCH 448/494] Minor auto-refactor code cleanup on a massive scale Original PR: https://github.com/LabKey/platform/pull/6742 * Minor auto-refactor code cleanup on a massive scale * Fix DomainDesignerTest, reduce usage of deprecated methods * Fix merge --- .../labkey/specimen/RequestedSpecimens.java | 2 +- .../specimen/SpecimenRequestManager.java | 2 +- .../labkey/specimen/SpecimenTableManager.java | 2 +- .../actions/ParticipantCommentAction.java | 3 +- .../actions/ShowUploadSpecimensAction.java | 10 +-- .../actions/SpecimenApiController.java | 4 +- .../specimen/actions/SpecimenController.java | 56 +++++++-------- .../specimen/actions/SpecimensViewBean.java | 2 +- .../importer/AbstractSpecimenTask.java | 3 - .../importer/RequestabilityManager.java | 10 +-- .../specimen/importer/SpecimenImporter.java | 10 +-- .../specimen/model/SpecimenTypeSummary.java | 2 +- .../ActorNotificationRecipientSet.java | 7 +- .../specimen/query/SpecimenQueryView.java | 6 +- .../query/SpecimenRequestQueryView.java | 4 +- .../specimen/query/SpecimenUpdateService.java | 4 +- .../report/SpecimenReportManager.java | 71 +++++++++---------- .../report/SpecimenTypeVisitReport.java | 12 ++-- .../specimen/report/SpecimenVisitReport.java | 14 ++-- .../ParticipantTypeReportFactory.java | 4 +- .../participant/ParticipantVisitReport.java | 6 +- .../request/RequestParticipantReport.java | 2 +- .../RequestParticipantReportFactory.java | 2 +- .../TypeParticipantReportFactory.java | 2 +- .../SpecimenRequestRequirement.java | 2 +- .../SpecimenRequestRequirementType.java | 4 +- .../settings/RequestNotificationSettings.java | 8 +-- .../org/labkey/specimen/view/manageActors.jsp | 4 +- .../labkey/specimen/view/manageStatuses.jsp | 4 +- .../src/org/labkey/specimen/view/specimen.jsp | 2 +- .../specimen/view/specimenVisitReport.jsp | 2 +- .../specimen/writer/SpecimenWriter.java | 1 - 32 files changed, 124 insertions(+), 143 deletions(-) diff --git a/specimen/src/org/labkey/specimen/RequestedSpecimens.java b/specimen/src/org/labkey/specimen/RequestedSpecimens.java index d03ad904..fb163329 100644 --- a/specimen/src/org/labkey/specimen/RequestedSpecimens.java +++ b/specimen/src/org/labkey/specimen/RequestedSpecimens.java @@ -39,7 +39,7 @@ public List getProvidingLocations() { if (_providingLocations == null) { - if (_vials == null || _vials.size() == 0) + if (_vials == null || _vials.isEmpty()) _providingLocations = Collections.emptyList(); else { diff --git a/specimen/src/org/labkey/specimen/SpecimenRequestManager.java b/specimen/src/org/labkey/specimen/SpecimenRequestManager.java index 5c94fa55..cafe543b 100644 --- a/specimen/src/org/labkey/specimen/SpecimenRequestManager.java +++ b/specimen/src/org/labkey/specimen/SpecimenRequestManager.java @@ -850,7 +850,7 @@ public Map getGroupedValueAllowedMap() public String[] getGroupedValueAllowedColumns() { Set keySet = _groupedValueAllowedColumnMap.keySet(); - String[] allowedColumns = keySet.toArray(new String[keySet.size()]); + String[] allowedColumns = keySet.toArray(new String[0]); Arrays.sort(allowedColumns, new ComparableComparator<>()); return allowedColumns; } diff --git a/specimen/src/org/labkey/specimen/SpecimenTableManager.java b/specimen/src/org/labkey/specimen/SpecimenTableManager.java index 1e9f4af2..c891966e 100644 --- a/specimen/src/org/labkey/specimen/SpecimenTableManager.java +++ b/specimen/src/org/labkey/specimen/SpecimenTableManager.java @@ -190,7 +190,7 @@ private String getTypeName(PropertyDescriptor property, SqlDialect dialect) if (null == typeName) typeName = dialect.getSqlTypeName(property.getJdbcType()); if (null == typeName) - throw new UnsupportedOperationException("Unsupported JdbcType: " + property.getJdbcType().toString()); + throw new UnsupportedOperationException("Unsupported JdbcType: " + property.getJdbcType()); if ("VARCHAR".equals(typeName)) typeName = String.format("VARCHAR(%d)", property.getScale()); return typeName; diff --git a/specimen/src/org/labkey/specimen/actions/ParticipantCommentAction.java b/specimen/src/org/labkey/specimen/actions/ParticipantCommentAction.java index f052a92d..a1380811 100644 --- a/specimen/src/org/labkey/specimen/actions/ParticipantCommentAction.java +++ b/specimen/src/org/labkey/specimen/actions/ParticipantCommentAction.java @@ -143,9 +143,8 @@ protected DataView createNewView(final ParticipantCommentForm form, QueryUpdateF commentProperty = ColumnInfo.legalNameFromName(commentProperty); Object values = updateForm.getOldValues(); - if (values instanceof Map) + if (values instanceof Map valueMap) { - Map valueMap = (Map)values; String oldComment = String.valueOf(valueMap.get(commentProperty)); String newComment = form.getComment(); diff --git a/specimen/src/org/labkey/specimen/actions/ShowUploadSpecimensAction.java b/specimen/src/org/labkey/specimen/actions/ShowUploadSpecimensAction.java index ea53ca90..5c8334e3 100644 --- a/specimen/src/org/labkey/specimen/actions/ShowUploadSpecimensAction.java +++ b/specimen/src/org/labkey/specimen/actions/ShowUploadSpecimensAction.java @@ -179,7 +179,7 @@ public boolean handlePost(UploadSpecimensForm form, BindException errors) throws Map> sampleIdMap = new HashMap<>(); String visitKey = study.getTimepointType() == TimepointType.VISIT ? SimpleSpecimenImporter.VISIT : SimpleSpecimenImporter.DRAW_TIMESTAMP; - if (specimenRows.size() == 0) + if (specimenRows.isEmpty()) errors.reject(SpringActionController.ERROR_MSG, "No specimen data was provided."); int rowNum = 1; for (Map row : specimenRows) @@ -209,7 +209,7 @@ public boolean handlePost(UploadSpecimensForm form, BindException errors) throws } if (null != sampleId) { - Pair participantVisit = new Pair(participant, row.get(visitKey)); + Pair participantVisit = new Pair<>(participant, row.get(visitKey)); if (sampleIdMap.containsKey(sampleId)) { if (!participantVisit.equals(sampleIdMap.get(sampleId))) @@ -264,10 +264,6 @@ else if (study.getTimepointType() == TimepointType.VISIT && null == row.get(Simp if (!errors.hasErrors()) importer.process(specimenRows, form.isMerge()); } - catch (IllegalStateException | ValidationException e) - { - errors.reject(SpringActionController.ERROR_MSG, e.getMessage()); - } catch (RuntimeSQLException e) { String message = e.getMessage(); @@ -275,7 +271,7 @@ else if (study.getTimepointType() == TimepointType.VISIT && null == row.get(Simp message = e.getSQLException().getNextException().getMessage(); errors.reject(SpringActionController.ERROR_MSG, "A database error was reported during import: " + message); } - catch (RuntimeException e) + catch (ValidationException | RuntimeException e) { errors.reject(SpringActionController.ERROR_MSG, e.getMessage()); } diff --git a/specimen/src/org/labkey/specimen/actions/SpecimenApiController.java b/specimen/src/org/labkey/specimen/actions/SpecimenApiController.java index 4294d241..e9a7dd6d 100644 --- a/specimen/src/org/labkey/specimen/actions/SpecimenApiController.java +++ b/specimen/src/org/labkey/specimen/actions/SpecimenApiController.java @@ -565,7 +565,7 @@ public ApiResponse execute(AddSpecimensToRequestForm addSpecimensToRequestForm, Set hashes = new HashSet<>(); Collections.addAll(hashes, addSpecimensToRequestForm.getSpecimenHashes()); RequestedSpecimens requested = SpecimenRequestManager.get().getRequestableBySpecimenHash(getContainer(), getUser(), hashes, addSpecimensToRequestForm.getPreferredLocation()); - if (requested.getVials().size() > 0) + if (!requested.getVials().isEmpty()) { List vials = new ArrayList<>(requested.getVials()); try @@ -663,7 +663,7 @@ public ApiResponse execute(SpecimenApiForm form, BindException errors) @RequiresPermission(ReadPermission.class) @ApiVersion(13.1) - public class GetSpecimenWebPartGroupsAction extends ReadOnlyApiAction + public static class GetSpecimenWebPartGroupsAction extends ReadOnlyApiAction { @Override public ApiResponse execute(SpecimenApiForm form, BindException errors) diff --git a/specimen/src/org/labkey/specimen/actions/SpecimenController.java b/specimen/src/org/labkey/specimen/actions/SpecimenController.java index d17a6e60..068edfb7 100644 --- a/specimen/src/org/labkey/specimen/actions/SpecimenController.java +++ b/specimen/src/org/labkey/specimen/actions/SpecimenController.java @@ -518,7 +518,6 @@ public SpecimenQueryView getSpecimenQueryView(boolean showVials, boolean forExpo if (addSep) { commentsMenuButton.addSeparator(); - addSep = false; } NavTree ptidComments = commentsMenuButton.addMenuItem("Manage " + PageFlowUtil.filter(subjectNoun) + "/Visit Comments", urlProvider(StudyUrls.class).getDatasetURL(getContainer(), participantVisitCommentDatasetId)); @@ -675,11 +674,11 @@ private static List getPossibleNotifications(Spec private String getShortSiteLabel(LocationImpl location) { String label; - if (location.getLabel() != null && location.getLabel().length() > 0) + if (location.getLabel() != null && !location.getLabel().isEmpty()) label = location.getLabel().substring(0, Math.min(location.getLabel().length(), 15)); else if (location.getLdmsLabCode() != null) label = "ldmsId" + location.getLdmsLabCode(); - else if (location.getLabwareLabCode() != null && location.getLabwareLabCode().length() > 0) + else if (location.getLabwareLabCode() != null && !location.getLabwareLabCode().isEmpty()) label = "labwareId" + location.getLabwareLabCode(); else label = "rowId" + location.getRowId(); @@ -688,10 +687,9 @@ else if (location.getLabwareLabCode() != null && location.getLabwareLabCode().le private String getSpecimenListFileName(LocationImpl srcLocation, LocationImpl destLocation) { - StringBuilder filename = new StringBuilder(); - filename.append(getShortSiteLabel(srcLocation)).append("_to_").append(getShortSiteLabel(destLocation)); - filename.append("_").append(DateUtil.formatIsoDate()); - return filename.toString(); + String filename = getShortSiteLabel(srcLocation) + "_to_" + getShortSiteLabel(destLocation) + + "_" + DateUtil.formatIsoDate(); + return filename; } public SimpleFilter getSpecimenListFilter(SpecimenRequest specimenRequest, LocationImpl srcLocation, LabSpecimenListsBean.Type type) @@ -929,7 +927,7 @@ public static void submitSpecimenBatch(Container c, User user, ActionURL url, Fi } @RequiresPermission(AdminPermission.class) - public class SubmitSpecimenBatchImportAction extends FormHandlerAction + public static class SubmitSpecimenBatchImportAction extends FormHandlerAction { @Override public void validateCommand(PipelineForm target, Errors errors) @@ -965,7 +963,7 @@ public ActionURL getSuccessURL(PipelineForm pipelineForm) * programmatically. */ @RequiresPermission(AdminPermission.class) - public class SubmitSpecimenImport extends FormHandlerAction + public static class SubmitSpecimenImport extends FormHandlerAction { @Override public void validateCommand(PipelineForm target, Errors errors) @@ -1401,7 +1399,7 @@ public ModelAndView getView(ViewEventForm viewEventForm, BindException errors) relevantRequests = queryView; } else - relevantRequests = new JspView("/org/labkey/specimen/view/relevantRequests.jsp"); + relevantRequests = new JspView<>("/org/labkey/specimen/view/relevantRequests.jsp"); relevantRequests.setTitle("Relevant Vial Requests"); vbox = new VBox(summaryView, vialHistoryView, relevantRequests); } @@ -1427,7 +1425,7 @@ public void addNavTrail(NavTree root) private boolean isNullOrBlank(String toCheck) { - return ((toCheck == null) || toCheck.equals("")); + return ((toCheck == null) || toCheck.isEmpty()); } @RequiresPermission(ManageNotificationsPermission.class) @@ -1437,7 +1435,7 @@ public class ManageNotificationsAction extends FormViewAction requestedSpecimens = getSpecimensFromRowIds(getSpecimenRowIds(), container, user); - if (requestedSpecimens != null && requestedSpecimens.size() > 0) + if (requestedSpecimens != null && !requestedSpecimens.isEmpty()) return new RequestedSpecimens(requestedSpecimens); Set ids; @@ -2270,7 +2268,7 @@ public SpecimensViewBean(ViewContext context, List vials, boolean showHist boolean showRecordSelectors, boolean disableLowVialIndicators, boolean restrictRecordSelectors) { _vials = vials; - if (vials != null && vials.size() > 0) + if (vials != null && !vials.isEmpty()) { _specimenQueryView = SpecimenQueryView.createView(context, vials, SpecimenQueryView.ViewType.VIALS); _specimenQueryView.setShowHistoryLinks(showHistoryLinks); @@ -2368,7 +2366,7 @@ public void validateCommand(CreateSpecimenRequestForm form, Errors errors) for (int i = 0; i < form.getInputs().length && !missingRequiredInput; i++) { - if (form.getRequired()[i] && (form.getInputs()[i] == null || form.getInputs()[i].length() == 0)) + if (form.getRequired()[i] && (form.getInputs()[i] == null || form.getInputs()[i].isEmpty())) missingRequiredInput = true; } @@ -2397,7 +2395,7 @@ public boolean handlePost(CreateSpecimenRequestForm form, BindException errors) if (i > 0) comments.append("\n\n"); comments.append(expectedInput.getTitle()).append(":\n"); - if (inputs[i] != null && inputs[i].length() > 0) + if (inputs[i] != null && !inputs[i].isEmpty()) comments.append(inputs[i]); else comments.append("[Not provided]"); @@ -2787,7 +2785,7 @@ public boolean handlePost(ActorEditForm form, BindException errors) { for (String label : labels) { - if (label == null || label.length() == 0) + if (label == null || label.isEmpty()) errors.reject(ERROR_MSG, "Actor name cannot be empty."); } } @@ -2812,7 +2810,7 @@ public boolean handlePost(ActorEditForm form, BindException errors) } } - if (form.getNewLabel() != null && form.getNewLabel().length() > 0) + if (form.getNewLabel() != null && !form.getNewLabel().isEmpty()) { SpecimenRequestActor actor = new SpecimenRequestActor(); actor.setLabel(form.getNewLabel()); @@ -2829,7 +2827,7 @@ public boolean handlePost(ActorEditForm form, BindException errors) @Override public ActionURL getSuccessURL(ActorEditForm form) { - if (form.getNextPage() != null && form.getNextPage().length() > 0) + if (form.getNextPage() != null && !form.getNextPage().isEmpty()) return new ActionURL(form.getNextPage()); else return getManageStudyURL(); @@ -2850,7 +2848,7 @@ public boolean handlePost(BulkEditForm form, BindException errors) ensureSpecimenRequestsConfigured(false); String order = form.getOrder(); - if (order != null && order.length() > 0) + if (order != null && !order.isEmpty()) { String[] rowIds = order.split(","); // get a map of id to actor objects before starting our updates; this prevents us from @@ -3040,7 +3038,7 @@ public boolean handlePost(StatusEditForm form, BindException errors) } } - if (form.getNewLabel() != null && form.getNewLabel().length() > 0) + if (form.getNewLabel() != null && !form.getNewLabel().isEmpty()) { SpecimenRequestStatus status = new SpecimenRequestStatus(); status.setLabel(form.getNewLabel()); @@ -3085,7 +3083,7 @@ public boolean handlePost(BulkEditForm form, BindException errors) ensureSpecimenRequestsConfigured(false); String order = form.getOrder(); - if (order != null && order.length() > 0) + if (order != null && !order.isEmpty()) { String[] rowIdStrings = order.split(","); int[] rowIds = new int[rowIdStrings.length]; @@ -3541,7 +3539,7 @@ protected int importData(DataLoader dl, FileStream file, String originalName, Ba i += 1; } - if (errorList.size() == 0) + if (errorList.isEmpty()) { // Get all the specimen objects. If it throws an exception then there was an error and we'll // root around to figure out what to report @@ -3565,7 +3563,7 @@ protected int importData(DataLoader dl, FileStream file, String originalName, Ba } } - if (errorList.size() == 0) + if (errorList.isEmpty()) { ArrayList vialList = new ArrayList<>(vials.size()); vialList.addAll(vials); @@ -3806,7 +3804,7 @@ public boolean handlePost(final ManageRequestStatusForm form, BindException erro List files = getAttachmentFileList(); boolean hasAttachments = !files.isEmpty(); - boolean hasComments = form.getComments() != null && form.getComments().length() > 0; + boolean hasComments = form.getComments() != null && !form.getComments().isEmpty(); if (statusChanged || detailsChanged || hasComments || hasAttachments) { RequestEventType changeType; @@ -4094,7 +4092,7 @@ public ModelAndView getView(UpdateParticipantCommentsForm specimenCommentsForm, { List selectedVials = getSpecimensFromPost(specimenCommentsForm.isFromGroupedView(), false); - if (selectedVials == null || selectedVials.size() == 0) + if (selectedVials == null || selectedVials.isEmpty()) { // are the vial IDs on the URL? int[] rowId = specimenCommentsForm.getRowId(); @@ -4745,7 +4743,7 @@ public boolean handlePost(final ManageRequirementForm form, BindException errors eventSummary = "Comment added."; } - if (form.getComment() != null && form.getComment().length() > 0) + if (form.getComment() != null && !form.getComment().isEmpty()) comment.append("\n").append(form.getComment()); SpecimenRequestEvent event; @@ -5272,7 +5270,7 @@ public void setActiveTransform(String activeTransform) } @RequiresPermission(AdminPermission.class) - public class ChooseImporterAction extends FormViewAction + public static class ChooseImporterAction extends FormViewAction { @Override public void addNavTrail(NavTree root) diff --git a/specimen/src/org/labkey/specimen/actions/SpecimensViewBean.java b/specimen/src/org/labkey/specimen/actions/SpecimensViewBean.java index c2ef7fb8..5cba6f1b 100644 --- a/specimen/src/org/labkey/specimen/actions/SpecimensViewBean.java +++ b/specimen/src/org/labkey/specimen/actions/SpecimensViewBean.java @@ -15,7 +15,7 @@ public SpecimensViewBean(ViewContext context, List vials, boolean showHist boolean showRecordSelectors, boolean disableLowVialIndicators, boolean restrictRecordSelectors) { _vials = vials; - if (vials != null && vials.size() > 0) + if (vials != null && !vials.isEmpty()) { _specimenQueryView = SpecimenQueryView.createView(context, vials, SpecimenQueryView.ViewType.VIALS); _specimenQueryView.setShowHistoryLinks(showHistoryLinks); diff --git a/specimen/src/org/labkey/specimen/importer/AbstractSpecimenTask.java b/specimen/src/org/labkey/specimen/importer/AbstractSpecimenTask.java index 4d27ca76..ff74eb52 100644 --- a/specimen/src/org/labkey/specimen/importer/AbstractSpecimenTask.java +++ b/specimen/src/org/labkey/specimen/importer/AbstractSpecimenTask.java @@ -28,10 +28,7 @@ import org.labkey.api.query.ValidationException; import org.labkey.api.study.SpecimenService; import org.labkey.api.study.SpecimenTransform; -import org.labkey.api.study.Study; -import org.labkey.api.study.StudyService; import org.labkey.api.study.importer.SimpleStudyImportContext; -import org.labkey.api.study.model.VisitService; import org.labkey.api.util.DateUtil; import org.labkey.api.util.FileType; import org.labkey.api.util.FileUtil; diff --git a/specimen/src/org/labkey/specimen/importer/RequestabilityManager.java b/specimen/src/org/labkey/specimen/importer/RequestabilityManager.java index 7e0f9aec..cc6ccdc0 100644 --- a/specimen/src/org/labkey/specimen/importer/RequestabilityManager.java +++ b/specimen/src/org/labkey/specimen/importer/RequestabilityManager.java @@ -498,7 +498,7 @@ protected CustomQueryRule(Container container, String state) _schemaName = values[0]; _queryName = values[1]; _viewName = values[2]; - if (_viewName != null && _viewName.length() == 0) + if (_viewName != null && _viewName.isEmpty()) _viewName = null; _markRequestable = Boolean.parseBoolean(values[3]); } @@ -600,7 +600,7 @@ public RepositoryRule(Container container) public SQLFragment getFilterSQL(Container container, User user, List vials) { SQLFragment sql = new SQLFragment("AtRepository = ?", Boolean.FALSE); - if (vials != null && vials.size() > 0) + if (vials != null && !vials.isEmpty()) sql.append(" AND ").append(getGlobalUniqueIdInSQL(vials)); return sql; } @@ -643,7 +643,7 @@ public RuleType getType() public SQLFragment getFilterSQL(Container container, User user, List vials) { SQLFragment sql = new SQLFragment("Requestable IS NOT NULL"); - if (vials != null && vials.size() > 0) + if (vials != null && !vials.isEmpty()) sql.append(" AND ").append(getGlobalUniqueIdInSQL(vials)); return sql; } @@ -666,7 +666,7 @@ public LockedInRequestRule(Container container) public SQLFragment getFilterSQL(Container container, User user, List vials) { SQLFragment sql = new SQLFragment("LockedInRequest = ?", Boolean.TRUE); - if (vials != null && vials.size() > 0) + if (vials != null && !vials.isEmpty()) sql.append(" AND ").append(getGlobalUniqueIdInSQL(vials)); return sql; } @@ -802,7 +802,7 @@ private void updateRequestability(Container container, User user, boolean resetT .append(" SET Available = ?, AvailabilityReason = ? "); updateSQL.add(Boolean.TRUE); updateSQL.add(null); - if (vials != null && vials.size() > 0) + if (vials != null && !vials.isEmpty()) { updateSQL.append("WHERE RowId IN ("); String sep = ""; diff --git a/specimen/src/org/labkey/specimen/importer/SpecimenImporter.java b/specimen/src/org/labkey/specimen/importer/SpecimenImporter.java index ac5e359d..80b69f1f 100644 --- a/specimen/src/org/labkey/specimen/importer/SpecimenImporter.java +++ b/specimen/src/org/labkey/specimen/importer/SpecimenImporter.java @@ -1785,7 +1785,6 @@ private void mergeTable(DbSchema schema, SpecimenImportFile file, TableInfo targ * @param schema The dbschema. * @param idCol The computed column. * @return A pair of the columns actually found in the data values and a total row count. - * @throws org.labkey.api.query.ValidationException */ private Pair, Integer> mergeTable( DbSchema schema, String tableName, @Nullable TableInfo target, @@ -1894,11 +1893,8 @@ private void replaceTable(DbSchema schema, SpecimenImportFile file, TableInfo ta * @param file SpecimenImportFile * @param tableName Fully qualified table name, e.g., "study.Vials" * @param generateGlobaluniqueIds Generate globalUniqueIds if any needed - * @param hasContainerColumn - * @param drawDate DrawDate column or null * @param drawTime DrawTime column or null * @return A pair of the columns actually found in the data values and a total row count. - * @throws IOException */ public Pair, Integer> replaceTable( DbSchema schema, SpecimenImportFile file, String tableName, @Nullable TableInfo target, @@ -2519,7 +2515,7 @@ protected void checkForConflictingSpecimens(DbSchema schema, String tempTable, L } } - if (duplicateGUIDs.size() == 0) + if (duplicateGUIDs.isEmpty()) { info("No conflicting specimens found"); } @@ -2529,9 +2525,9 @@ protected void checkForConflictingSpecimens(DbSchema schema, String tempTable, L for (String guid : duplicateGUIDs) { List> dups = rowsByGUID.get(guid); - if (dups != null && dups.size() > 0) + if (dups != null && !dups.isEmpty()) { - if (sb.length() > 0) + if (!sb.isEmpty()) sb.append("\n"); sb.append("Conflicting specimens found for GlobalUniqueId '").append(guid).append("':\n"); diff --git a/specimen/src/org/labkey/specimen/model/SpecimenTypeSummary.java b/specimen/src/org/labkey/specimen/model/SpecimenTypeSummary.java index af92f00d..ea196125 100644 --- a/specimen/src/org/labkey/specimen/model/SpecimenTypeSummary.java +++ b/specimen/src/org/labkey/specimen/model/SpecimenTypeSummary.java @@ -263,7 +263,7 @@ public List getAdditives(PrimaryTypeCount primaryType, Deri if (counts.size() == 1) { String label = counts.values().iterator().next().getLabel(); - if (label == null || label.length() == 0 || "None".equalsIgnoreCase(label)) + if (label == null || label.isEmpty() || "None".equalsIgnoreCase(label)) return Collections.emptyList(); } return new ArrayList<>(counts.values()); diff --git a/specimen/src/org/labkey/specimen/notifications/ActorNotificationRecipientSet.java b/specimen/src/org/labkey/specimen/notifications/ActorNotificationRecipientSet.java index ef1709d3..a11ad3ba 100644 --- a/specimen/src/org/labkey/specimen/notifications/ActorNotificationRecipientSet.java +++ b/specimen/src/org/labkey/specimen/notifications/ActorNotificationRecipientSet.java @@ -81,10 +81,9 @@ public String getShortRecipientDescription() @Override public String getLongRecipientDescription() { - StringBuilder emailList = new StringBuilder(getShortRecipientDescription()); - emailList.append(": "); - emailList.append(getEmailAddressesAsString(", ")); - return emailList.toString(); + String emailList = getShortRecipientDescription() + ": " + + getEmailAddressesAsString(", "); + return emailList; } public String getFormValue() diff --git a/specimen/src/org/labkey/specimen/query/SpecimenQueryView.java b/specimen/src/org/labkey/specimen/query/SpecimenQueryView.java index 3198ca88..19a13a49 100644 --- a/specimen/src/org/labkey/specimen/query/SpecimenQueryView.java +++ b/specimen/src/org/labkey/specimen/query/SpecimenQueryView.java @@ -607,7 +607,7 @@ private static Sort createDefaultSort(ViewType viewType) protected static SimpleFilter addFilterClause(SimpleFilter filter, List vials, ViewType viewType) { - if (vials != null && vials.size() > 0) + if (vials != null && !vials.isEmpty()) { StringBuilder whereClause = new StringBuilder(); if (viewType.isVialView()) @@ -657,7 +657,7 @@ else if (pd.getVisitDate() != null) params.add(pd.getParticipantId()); sep = " OR "; } - filter = filter.addWhereClause(whereClause.toString(), params.toArray(new Object[params.size()])); + filter = filter.addWhereClause(whereClause.toString(), params.toArray(new Object[0])); } return filter; } @@ -672,7 +672,7 @@ protected static SimpleFilter addNotPreviouslyRequestedClause(SimpleFilter filte + "join study.SamplerequestStatus status ON r.StatusId=status.RowId " + "where r.DestinationSiteId=").appendValue(locationId).append(" AND status.SpecimensLocked=").append(tableInfoVial.getSqlDialect().getBooleanTRUE()).append(")"); - assert(0 == sql.getParams().size()); + assert(sql.getParams().isEmpty()); filter.addWhereClause(sql.getSQL(), null, FieldKey.fromParts("SpecimenHash")); diff --git a/specimen/src/org/labkey/specimen/query/SpecimenRequestQueryView.java b/specimen/src/org/labkey/specimen/query/SpecimenRequestQueryView.java index 65b6d38a..6799b910 100644 --- a/specimen/src/org/labkey/specimen/query/SpecimenRequestQueryView.java +++ b/specimen/src/org/labkey/specimen/query/SpecimenRequestQueryView.java @@ -119,8 +119,8 @@ public void renderGridCellContents(RenderContext ctx, HtmlWriter out) if (statusId.intValue() == _shoppingCartStatusRowId.intValue() && canEdit) { - ActionURL submitUrl = new ActionURL(SubmitRequestAction.class, ctx.getContainer()).addParameter("id", "${requestId}");; - ActionURL cancelUrl = new ActionURL(DeleteRequestAction.class, ctx.getContainer()).addParameter("id", "${requestId}");; + ActionURL submitUrl = new ActionURL(SubmitRequestAction.class, ctx.getContainer()).addParameter("id", "${requestId}"); + ActionURL cancelUrl = new ActionURL(DeleteRequestAction.class, ctx.getContainer()).addParameter("id", "${requestId}"); content.append(PageFlowUtil.button("Submit").href(submitUrl).usePost(StudyUtils.SUBMISSION_WARNING)); content.append(PageFlowUtil.button("Cancel").href(cancelUrl).usePost(StudyUtils.CANCELLATION_WARNING)); diff --git a/specimen/src/org/labkey/specimen/query/SpecimenUpdateService.java b/specimen/src/org/labkey/specimen/query/SpecimenUpdateService.java index d7285c9a..99192ede 100644 --- a/specimen/src/org/labkey/specimen/query/SpecimenUpdateService.java +++ b/specimen/src/org/labkey/specimen/query/SpecimenUpdateService.java @@ -472,7 +472,7 @@ private long keyFromMap(Map map) throws InvalidKeyException rowId = map.get("rowid"); if (null == rowId) throw new InvalidKeyException("No value provided for 'rowId' column"); - Long rowLong = (Long)new LongConverter(null).convert(Long.class, rowId); + Long rowLong = new LongConverter(null).convert(Long.class, rowId); if (null == rowLong) throw new InvalidKeyException("Unable to convert rowId of '" + rowId + "' to a long"); return rowLong; @@ -516,7 +516,7 @@ private void checkEditability(Container container, Vial vial) throws ValidationE ArrayList counts = new SqlSelector(getQueryTable().getSchema(), sql).getArrayList(Integer.class); if (counts.size() > 1) throw new IllegalStateException("Expected one and only one count of rows."); - else if (counts.size() > 0 && counts.get(0) != 0) + else if (!counts.isEmpty() && counts.get(0) != 0) throw new ValidationException("Specimen may not be edited when it's in a non-final request."); } diff --git a/specimen/src/org/labkey/specimen/report/SpecimenReportManager.java b/specimen/src/org/labkey/specimen/report/SpecimenReportManager.java index bc48878f..599a7cce 100644 --- a/specimen/src/org/labkey/specimen/report/SpecimenReportManager.java +++ b/specimen/src/org/labkey/specimen/report/SpecimenReportManager.java @@ -87,7 +87,7 @@ private SpecimenDetailQueryHelper getSpecimenDetailQueryHelper(Container contain Collection cols = new ArrayList<>(colMap.values()); cols = QueryService.get().ensureRequiredColumns(tinfo, cols, specimenDetailFilter, null, unresolvedColumns); if (!unresolvedColumns.isEmpty()) - throw new IllegalStateException("Unable to resolve column(s): " + unresolvedColumns.toString()); + throw new IllegalStateException("Unable to resolve column(s): " + unresolvedColumns); // generate our select SQL: SQLFragment viewSql = new SQLFragment(); viewSql.appendComment("", tinfo.getSqlDialect()); @@ -148,10 +148,10 @@ public SummaryByVisitType[] getSpecimenSummaryByVisitType(Container container, U if (rs.getObject("Visit") != null) summary.setVisit(rs.getInt("Visit")); summary.setTotalVolume(rs.getDouble("TotalVolume")); - Double vialCount = rs.getDouble("VialCount"); - summary.setVialCount(vialCount.longValue()); - Double participantCount = rs.getDouble("ParticipantCount"); - summary.setParticipantCount(participantCount.longValue()); + long vialCount = rs.getLong("VialCount"); + summary.setVialCount(vialCount); + long participantCount = rs.getLong("ParticipantCount"); + summary.setParticipantCount(participantCount); for (Map.Entry typeProperty : viewSqlHelper.getAliasToTypePropertyMap().entrySet()) { @@ -168,7 +168,7 @@ public SummaryByVisitType[] getSpecimenSummaryByVisitType(Container container, U ret.add(summary); }); - SummaryByVisitType[] summaries = ret.toArray(new SummaryByVisitType[ret.size()]); + SummaryByVisitType[] summaries = ret.toArray(new SummaryByVisitType[0]); if (includeParticipantGroups) setSummaryParticipantGroups(perPtidSpecimenSQL, viewSqlHelper.getViewSql().getParamsArray(), @@ -291,8 +291,8 @@ public RequestSummaryByVisitType[] getRequestSummaryBySite(Container container, summary.setSiteLabel(rs.getString("SiteLabel")); summary.setVisit(rs.getInt("Visit")); summary.setTotalVolume(rs.getDouble("TotalVolume")); - Double vialCount = rs.getDouble("VialCount"); - summary.setVialCount(vialCount.longValue()); + long vialCount = rs.getLong("VialCount"); + summary.setVialCount(vialCount); for (Map.Entry typeProperty : sqlHelper.getAliasToTypePropertyMap().entrySet()) { @@ -310,7 +310,7 @@ public RequestSummaryByVisitType[] getRequestSummaryBySite(Container container, ret.add(summary); }); - RequestSummaryByVisitType[] summaries = ret.toArray(new RequestSummaryByVisitType[ret.size()]); + RequestSummaryByVisitType[] summaries = ret.toArray(new RequestSummaryByVisitType[0]); if (includeParticipantGroups) setSummaryParticipantGroups(sql, params, null, summaries, subjectCol, "Visit"); @@ -330,35 +330,32 @@ public Collection getParticipantSummaryByVisitType(Co } SpecimenDetailQueryHelper sqlHelper = getSpecimenDetailQueryHelper(container, user, baseView, specimenDetailFilter, null); String subjectCol = StudyService.get().getSubjectColumnName(container); - SQLFragment cohortJoinClause = null; - switch (cohortType) + SQLFragment cohortJoinClause = switch (cohortType) { - case DATA_COLLECTION: - cohortJoinClause = new SQLFragment( - "LEFT OUTER JOIN study.ParticipantVisit ON\n " + - "\tSpecimenQuery.SequenceNum = study.ParticipantVisit.SequenceNum AND\n" + - "\tSpecimenQuery." + subjectCol + " = study.ParticipantVisit.ParticipantId AND\n" + - "\tSpecimenQuery.Container = study.ParticipantVisit.Container\n" + - "LEFT OUTER JOIN study.Cohort ON \n" + - "\tstudy.ParticipantVisit.CohortId = study.Cohort.RowId AND\n" + - "\tstudy.ParticipantVisit.Container = study.Cohort.Container\n" - ); - break; - case PTID_CURRENT: - cohortJoinClause = new SQLFragment( - "LEFT OUTER JOIN study.Cohort ON \n" + - "\tstudy.Participant.CurrentCohortId = study.Cohort.RowId AND\n" + - "\tstudy.Participant.Container = study.Cohort.Container\n" - ); - break; - case PTID_INITIAL: - cohortJoinClause = new SQLFragment( - "LEFT OUTER JOIN study.Cohort ON \n" + - "\tstudy.Participant.InitialCohortId = study.Cohort.RowId AND\n" + - "\tstudy.Participant.Container = study.Cohort.Container\n" - ); - break; - } + case DATA_COLLECTION -> new SQLFragment( + "LEFT OUTER JOIN study.ParticipantVisit ON\n " + + "\tSpecimenQuery.SequenceNum = study.ParticipantVisit.SequenceNum AND\n" + + "\tSpecimenQuery." + subjectCol + " = study.ParticipantVisit.ParticipantId AND\n" + + "\tSpecimenQuery.Container = study.ParticipantVisit.Container\n" + + "LEFT OUTER JOIN study.Cohort ON \n" + + "\tstudy.ParticipantVisit.CohortId = study.Cohort.RowId AND\n" + + "\tstudy.ParticipantVisit.Container = study.Cohort.Container\n" + ); + case PTID_CURRENT -> new SQLFragment( + """ + LEFT OUTER JOIN study.Cohort ON\s + \tstudy.Participant.CurrentCohortId = study.Cohort.RowId AND + \tstudy.Participant.Container = study.Cohort.Container + """ + ); + case PTID_INITIAL -> new SQLFragment( + """ + LEFT OUTER JOIN study.Cohort ON\s + \tstudy.Participant.InitialCohortId = study.Cohort.RowId AND + \tstudy.Participant.Container = study.Cohort.Container + """ + ); + }; SQLFragment ptidSpecimenSQL = new SQLFragment(); SqlDialect d = SpecimenSchema.get().getSqlDialect(); diff --git a/specimen/src/org/labkey/specimen/report/SpecimenTypeVisitReport.java b/specimen/src/org/labkey/specimen/report/SpecimenTypeVisitReport.java index e3b93a14..61d5899c 100644 --- a/specimen/src/org/labkey/specimen/report/SpecimenTypeVisitReport.java +++ b/specimen/src/org/labkey/specimen/report/SpecimenTypeVisitReport.java @@ -69,13 +69,13 @@ private String getCellSummaryText(SummaryByVisitType summary) summaryString.append(summary.getVialCount()); if (isViewParticipantCount()) { - if (summaryString.length() > 0) + if (!summaryString.isEmpty()) summaryString.append("/"); summaryString.append(summary.getParticipantCount()); } if (isViewVolume()) { - if (summaryString.length() > 0) + if (!summaryString.isEmpty()) summaryString.append("/"); //summaryString.append(summary.getTotalVolume()); summaryString.append(Formats.f2.format(summary.getTotalVolume())); @@ -89,7 +89,7 @@ protected String[] getCellExcelText(Visit visit, SummaryByVisitType summary) if (summary == null || summary.getVialCount() == null) return new String[] {}; String summaryText = getCellSummaryText(summary); - boolean hasSummaryText = summaryText != null && summaryText.length() > 0; + boolean hasSummaryText = summaryText != null && !summaryText.isEmpty(); int ptidCount = isViewPtidList() ? summary.getParticipantIds().size() : 0; String[] strArray = new String[ptidCount + (hasSummaryText ? 1 : 0)]; if (hasSummaryText) @@ -123,7 +123,7 @@ protected String getCellHtml(Visit visit, SummaryByVisitType summary) linkHtml += "&" + getFilterQueryString(visit, summary); String summaryString = getCellSummaryText(summary); StringBuilder cellHtml = new StringBuilder(); - if (summaryString.length() > 0) + if (!summaryString.isEmpty()) { cellHtml.append(""); cellHtml.append(summaryString).append(""); @@ -131,7 +131,7 @@ protected String getCellHtml(Visit visit, SummaryByVisitType summary) if (isViewPtidList()) { - if (cellHtml.length() > 0) + if (!cellHtml.isEmpty()) cellHtml.append("
"); if (summary.getParticipantIds() != null) { @@ -145,7 +145,7 @@ protected String getCellHtml(Visit visit, SummaryByVisitType summary) url.addParameter(SpecimenVisitReportParameters.PARAMS.viewVialCount, isViewVialCount()); url.addParameter(SpecimenVisitReportParameters.PARAMS.viewParticipantCount, isViewParticipantCount()); url.addParameter(SpecimenVisitReportParameters.PARAMS.viewVolume, isViewVolume()); - if (getBaseCustomViewName() != null && getBaseCustomViewName().length() > 0 && + if (getBaseCustomViewName() != null && !getBaseCustomViewName().isEmpty() && !SpecimenVisitReportParameters.DEFAULT_VIEW_ID.equals(getBaseCustomViewName())) { url.addParameter(SpecimenVisitReportParameters.PARAMS.baseCustomViewName, getBaseCustomViewName()); diff --git a/specimen/src/org/labkey/specimen/report/SpecimenVisitReport.java b/specimen/src/org/labkey/specimen/report/SpecimenVisitReport.java index 6200ca40..9357a5a4 100644 --- a/specimen/src/org/labkey/specimen/report/SpecimenVisitReport.java +++ b/specimen/src/org/labkey/specimen/report/SpecimenVisitReport.java @@ -148,17 +148,17 @@ private String getTitleSuffix() suffixBuilder.append(" (Vial Count"); if (isViewVolume()) { - suffixBuilder.append(suffixBuilder.length() > 0 ? "/" : " ("); + suffixBuilder.append(!suffixBuilder.isEmpty() ? "/" : " ("); suffixBuilder.append("Total Volume"); } if (isViewParticipantCount()) { - suffixBuilder.append(suffixBuilder.length() > 0 ? "/" : " ("); + suffixBuilder.append(!suffixBuilder.isEmpty() ? "/" : " ("); suffixBuilder.append(PageFlowUtil.filter(StudyService.get().getSubjectNounSingular(getContainer()))).append(" Count"); } if (isViewPtidList()) { - suffixBuilder.append(suffixBuilder.length() > 0 ? ", " : " ("); + suffixBuilder.append(!suffixBuilder.isEmpty() ? ", " : " ("); suffixBuilder.append(PageFlowUtil.filter(StudyService.get().getSubjectColumnName(getContainer()))).append(" Group"); } suffixBuilder.append(")"); @@ -188,7 +188,7 @@ protected void addCohortURLFilter(Study study, ActionURL url) protected String getFilterQueryString(Visit visit, CELLDATA summary) { ActionURL url = new ActionURL(); - if (_parameters.getBaseCustomViewName() != null && _parameters.getBaseCustomViewName().length() > 0) + if (_parameters.getBaseCustomViewName() != null && !_parameters.getBaseCustomViewName().isEmpty()) { if (!SpecimenVisitReportParameters.DEFAULT_VIEW_ID.equals(_parameters.getBaseCustomViewName())) url.addParameter("SpecimenDetail.viewName", _parameters.getBaseCustomViewName()); @@ -227,7 +227,7 @@ protected String getFilterQueryString(Visit visit, CELLDATA summary) } String baseParams = geBaseViewFilter().toQueryString("SpecimenDetail"); - if (baseParams != null && baseParams.length() > 0) + if (baseParams != null && !baseParams.isEmpty()) return baseParams + "&" + url.getQueryString(); else return url.getQueryString(); @@ -326,7 +326,7 @@ protected boolean hasContent(String[] strArray) return false; for (String str : strArray) { - if (str != null && str.length() > 0) + if (str != null && !str.isEmpty()) return true; } return false; @@ -343,7 +343,7 @@ protected String buildCellHtml(Visit visit, CELLDATA summary, String linkHtml) boolean first = true; for (String str : summaryString) { - if (str != null && str.length() > 0) + if (str != null && !str.isEmpty()) { if (!first) cellHtml.append("
"); diff --git a/specimen/src/org/labkey/specimen/report/participant/ParticipantTypeReportFactory.java b/specimen/src/org/labkey/specimen/report/participant/ParticipantTypeReportFactory.java index 3ad79ee5..3fdec4f8 100644 --- a/specimen/src/org/labkey/specimen/report/participant/ParticipantTypeReportFactory.java +++ b/specimen/src/org/labkey/specimen/report/participant/ParticipantTypeReportFactory.java @@ -147,7 +147,7 @@ protected List getSelectedTypes() private String getLabel(SpecimenTypeSummary.TypeCount type) { - if (type.getLabel() == null || type.getLabel().length() == 0) + if (type.getLabel() == null || type.getLabel().isEmpty()) return "[unknown]"; else return type.getLabel(); @@ -159,7 +159,7 @@ private SpecimenTypeSummary.TypeCount getTypeCountByLabel(List createRows() for (SummaryByVisitParticipant count : countSummary) { String cohort = _showCohorts ? count.getCohort() : "[cohort blinded]"; - if (cohort == null || cohort.length() == 0) + if (cohort == null || cohort.isEmpty()) cohort = "[No cohort assigned]"; String key = cohort + "/" + count.getParticipantId(); Row row = rows.get(key); @@ -96,7 +96,7 @@ protected String[] getCellExcelText(Visit visit, SummaryByVisitParticipant summa summaryString.append(summary.getVialCount()); if (isViewVolume()) { - if (summaryString.length() > 0) + if (!summaryString.isEmpty()) summaryString.append("/"); summaryString.append(summary.getTotalVolume()); } @@ -115,7 +115,7 @@ protected String getCellHtml(Visit visit, SummaryByVisitParticipant summary) if (_filter != null) { String filterQueryString = getFilterQueryString(visit, summary); - if (filterQueryString != null && filterQueryString.length() > 0) + if (filterQueryString != null && !filterQueryString.isEmpty()) linkHtml += "&" + filterQueryString; } return buildCellHtml(visit, summary, linkHtml); diff --git a/specimen/src/org/labkey/specimen/report/request/RequestParticipantReport.java b/specimen/src/org/labkey/specimen/report/request/RequestParticipantReport.java index 76a90105..5cd207a1 100644 --- a/specimen/src/org/labkey/specimen/report/request/RequestParticipantReport.java +++ b/specimen/src/org/labkey/specimen/report/request/RequestParticipantReport.java @@ -78,7 +78,7 @@ protected String[] getCellExcelText(Visit visit, RequestSummaryByVisitType summa summaryString.append(summary.getVialCount()); if (isViewVolume()) { - if (summaryString.length() > 0) + if (!summaryString.isEmpty()) summaryString.append("/"); summaryString.append(summary.getTotalVolume()); } diff --git a/specimen/src/org/labkey/specimen/report/request/RequestParticipantReportFactory.java b/specimen/src/org/labkey/specimen/report/request/RequestParticipantReportFactory.java index 82e7b7ea..68cc1e76 100644 --- a/specimen/src/org/labkey/specimen/report/request/RequestParticipantReportFactory.java +++ b/specimen/src/org/labkey/specimen/report/request/RequestParticipantReportFactory.java @@ -94,7 +94,7 @@ public List> getAdditionalFormInputHtml(User user) protected List createReports() { Collection participantIds; - if (!isAllSubjectsOption(_participantId) && _participantId != null && _participantId.trim().length() > 0) + if (!isAllSubjectsOption(_participantId) && _participantId != null && !_participantId.trim().isEmpty()) { Study study = StudyService.get().getStudy(getContainer()); if (!StudyService.get().participantExists(study, _participantId)) diff --git a/specimen/src/org/labkey/specimen/report/specimentype/TypeParticipantReportFactory.java b/specimen/src/org/labkey/specimen/report/specimentype/TypeParticipantReportFactory.java index cbcb00b9..4105d949 100644 --- a/specimen/src/org/labkey/specimen/report/specimentype/TypeParticipantReportFactory.java +++ b/specimen/src/org/labkey/specimen/report/specimentype/TypeParticipantReportFactory.java @@ -84,7 +84,7 @@ public boolean allowsParticipantAggregates() protected List createReports() { Collection participantIds; - if (!isAllSubjectsOption(_participantId) && _participantId != null && _participantId.trim().length() > 0) + if (!isAllSubjectsOption(_participantId) && _participantId != null && !_participantId.trim().isEmpty()) { Study study = StudyService.get().getStudy(getContainer()); if (!StudyService.get().participantExists(study, _participantId)) diff --git a/specimen/src/org/labkey/specimen/requirements/SpecimenRequestRequirement.java b/specimen/src/org/labkey/specimen/requirements/SpecimenRequestRequirement.java index 20f560ab..edd50de3 100644 --- a/specimen/src/org/labkey/specimen/requirements/SpecimenRequestRequirement.java +++ b/specimen/src/org/labkey/specimen/requirements/SpecimenRequestRequirement.java @@ -188,7 +188,7 @@ public String getRequirementSummary() builder.append(" (").append(getLocation().getLabel()).append(")"); if (_description != null) { - if (builder.length() > 0 && _description.length() > 0) + if (!builder.isEmpty() && !_description.isEmpty()) builder.append(", "); builder.append(_description); } diff --git a/specimen/src/org/labkey/specimen/requirements/SpecimenRequestRequirementType.java b/specimen/src/org/labkey/specimen/requirements/SpecimenRequestRequirementType.java index 660fc013..3595d1e4 100644 --- a/specimen/src/org/labkey/specimen/requirements/SpecimenRequestRequirementType.java +++ b/specimen/src/org/labkey/specimen/requirements/SpecimenRequestRequirementType.java @@ -40,7 +40,7 @@ public List generateRequirements(SpecimenRequest own { List requirements = new ArrayList<>(); List vials = owner.getVials(); - if (vials.size() > 0) + if (!vials.isEmpty()) { // get a list of all providing and originating sites: Set originatingLocationIds = new HashSet<>(); @@ -68,7 +68,7 @@ public List generateRequirements(SpecimenRequest own { List requirements = new ArrayList<>(); List vials = owner.getVials(); - if (vials.size() > 0) + if (!vials.isEmpty()) { // get a list of all providing and originating sites: Set providerLocationIds = new HashSet<>(); diff --git a/specimen/src/org/labkey/specimen/settings/RequestNotificationSettings.java b/specimen/src/org/labkey/specimen/settings/RequestNotificationSettings.java index 10ed391e..a3db5300 100644 --- a/specimen/src/org/labkey/specimen/settings/RequestNotificationSettings.java +++ b/specimen/src/org/labkey/specimen/settings/RequestNotificationSettings.java @@ -155,7 +155,7 @@ public static RequestNotificationSettings getDefaultSettings(Container c) public Address[] getCCAddresses() throws ValidEmail.InvalidEmailException { - if (_cc == null || _cc.length() == 0) + if (_cc == null || _cc.isEmpty()) return null; StringTokenizer splitter = new StringTokenizer(_cc, ",;:\t\n\r"); List
addresses = new ArrayList<>(); @@ -165,12 +165,12 @@ public Address[] getCCAddresses() throws ValidEmail.InvalidEmailException ValidEmail tester = new ValidEmail(token); addresses.add(tester.getAddress()); } - return addresses.toArray(new Address[addresses.size()]); + return addresses.toArray(new Address[0]); } public Address[] getNewRequestNotifyAddresses() throws ValidEmail.InvalidEmailException { - if (_newRequestNotify == null || _newRequestNotify.length() == 0) + if (_newRequestNotify == null || _newRequestNotify.isEmpty()) return null; StringTokenizer splitter = new StringTokenizer(_newRequestNotify, ",;:\t\n\r"); List
addresses = new ArrayList<>(); @@ -180,7 +180,7 @@ public Address[] getNewRequestNotifyAddresses() throws ValidEmail.InvalidEmailEx ValidEmail tester = new ValidEmail(token); addresses.add(tester.getAddress()); } - return addresses.toArray(new Address[addresses.size()]); + return addresses.toArray(new Address[0]); } public boolean isReplyToCurrentUser() diff --git a/specimen/src/org/labkey/specimen/view/manageActors.jsp b/specimen/src/org/labkey/specimen/view/manageActors.jsp index da39822f..2574d5c3 100644 --- a/specimen/src/org/labkey/specimen/view/manageActors.jsp +++ b/specimen/src/org/labkey/specimen/view/manageActors.jsp @@ -38,7 +38,7 @@ String showMemberSitesIdStr = request.getParameter("showMemberSites"); int showMemberSitesId = -1; Set inUseActorIds = SpecimenRequestRequirementProvider.get().getActorsInUseSet(getContainer()); - if (showMemberSitesIdStr != null && showMemberSitesIdStr.length() > 0) + if (showMemberSitesIdStr != null && !showMemberSitesIdStr.isEmpty()) { try { @@ -85,7 +85,7 @@ <% if (showMemberSitesId == actor.getRowId()) { - if (study.getLocations().size() > 0) + if (!study.getLocations().isEmpty()) { %> Choose Site:
diff --git a/specimen/src/org/labkey/specimen/view/manageStatuses.jsp b/specimen/src/org/labkey/specimen/view/manageStatuses.jsp index 7fc51da3..73a5aa25 100644 --- a/specimen/src/org/labkey/specimen/view/manageStatuses.jsp +++ b/specimen/src/org/labkey/specimen/view/manageStatuses.jsp @@ -56,14 +56,14 @@ function showSystemRows(value)
<%= link("View Request").href(SpecimenController.getManageRequestURL(getContainer(), requirement.getRequestId(), null))%><%= link("View Request", SpecimenController.getManageRequestURL(getContainer(), requirement.getRequestId(), null))%>
<%=link("delete").href(urlFor(DeleteStatusAction.class).addParameter("id", status.getRowId())).usePost()%><%=link("delete", urlFor(DeleteStatusAction.class).addParameter("id", status.getRowId())).usePost()%>
- + <% - if (statuses != null && statuses.size() > 0) + if (statuses != null && !statuses.isEmpty()) { for(SpecimenRequestStatus status : statuses) { diff --git a/specimen/src/org/labkey/specimen/view/specimen.jsp b/specimen/src/org/labkey/specimen/view/specimen.jsp index 97327250..4d9a52eb 100644 --- a/specimen/src/org/labkey/specimen/view/specimen.jsp +++ b/specimen/src/org/labkey/specimen/view/specimen.jsp @@ -83,7 +83,7 @@ if (comment != null) Vial is flagged for quality control. <% } - if (comment.getQualityControlComments() != null && comment.getQualityControlComments().length() > 0) + if (comment.getQualityControlComments() != null && !comment.getQualityControlComments().isEmpty()) { %> <%= h(comment.getQualityControlComments()) %>
diff --git a/specimen/src/org/labkey/specimen/view/specimenVisitReport.jsp b/specimen/src/org/labkey/specimen/view/specimenVisitReport.jsp index f8e56d72..2771142a 100644 --- a/specimen/src/org/labkey/specimen/view/specimenVisitReport.jsp +++ b/specimen/src/org/labkey/specimen/view/specimenVisitReport.jsp @@ -125,7 +125,7 @@ The request has produced no records. for (int col = 0; col Date: Wed, 25 Jun 2025 23:26:18 +0100 Subject: [PATCH 449/494] Option to expose read-only specimen data without the module Original PR: https://github.com/LabKey/platform/pull/6778 --- .../org/labkey/specimen/SpecimenManager.java | 17 +++++++++-------- .../src/org/labkey/specimen/SpecimenModule.java | 9 +++++---- .../labkey/specimen/SpecimenRequestManager.java | 3 ++- .../specimen/actions/SpecimenController.java | 5 +++-- .../specimen/query/SpecimenQueryView.java | 3 ++- .../specimen/report/SpecimenReportManager.java | 3 ++- .../specimen/writer/SpecimenArchiveWriter.java | 12 ++++++------ 7 files changed, 29 insertions(+), 23 deletions(-) diff --git a/specimen/src/org/labkey/specimen/SpecimenManager.java b/specimen/src/org/labkey/specimen/SpecimenManager.java index 2a0aea40..31025d82 100644 --- a/specimen/src/org/labkey/specimen/SpecimenManager.java +++ b/specimen/src/org/labkey/specimen/SpecimenManager.java @@ -28,6 +28,7 @@ import org.labkey.api.specimen.location.LocationImpl; import org.labkey.api.specimen.location.LocationManager; import org.labkey.api.specimen.model.SpecimenComment; +import org.labkey.api.specimen.model.SpecimenTablesProvider; import org.labkey.specimen.model.SpecimenTypeSummary; import org.labkey.specimen.model.SpecimenTypeSummaryRow; import org.labkey.api.study.Cohort; @@ -73,7 +74,7 @@ public List getVisitsWithSpecimens(Container container, User us { Study study = StudyService.get().getStudy(container); UserSchema schema = SpecimenQuerySchema.get(study, user); - TableInfo tinfo = schema.getTable(SpecimenQuerySchema.SIMPLE_SPECIMEN_TABLE_NAME); + TableInfo tinfo = schema.getTable(SpecimenTablesProvider.SIMPLE_SPECIMEN_TABLE_NAME); FieldKey visitKey = FieldKey.fromParts("Visit"); Map colMap = QueryService.get().getColumns(tinfo, Collections.singleton(visitKey)); @@ -114,7 +115,7 @@ public LocationImpl[] getSitesWithRequests(Container container) public Set getEnrollmentSitesWithRequests(Container container, User user) { UserSchema schema = SpecimenQuerySchema.get(StudyService.get().getStudy(container), user); - TableInfo tableInfoSpecimenDetail = schema.getTable(SpecimenQuerySchema.SPECIMEN_WRAP_TABLE_NAME); + TableInfo tableInfoSpecimenDetail = schema.getTable(SpecimenTablesProvider.SPECIMEN_WRAP_TABLE_NAME); if (null == tableInfoSpecimenDetail) throw new IllegalStateException("SpecimenDetail table not found."); String tableInfoAlias = "Specimen"; @@ -143,7 +144,7 @@ public Set getEnrollmentSitesWithRequests(Container container, Use public Set getEnrollmentSitesWithSpecimens(Container container, User user) { UserSchema schema = SpecimenQuerySchema.get(StudyService.get().getStudy(container), user); - TableInfo tableInfoSpecimenDetail = schema.getTable(SpecimenQuerySchema.SPECIMEN_WRAP_TABLE_NAME); + TableInfo tableInfoSpecimenDetail = schema.getTable(SpecimenTablesProvider.SPECIMEN_WRAP_TABLE_NAME); if (null == tableInfoSpecimenDetail) throw new IllegalStateException("SpecimenDetail table not found."); String tableInfoAlias = "Specimen"; @@ -517,7 +518,7 @@ public TableSelector getSpecimensSelector(final Container container, final User throw new NotFoundException("No study in container " + container.getPath()); } UserSchema schema = SpecimenQuerySchema.get(study, user); - TableInfo specimenTable = schema.getTable(SpecimenQuerySchema.SPECIMEN_WRAP_TABLE_NAME); + TableInfo specimenTable = schema.getTable(SpecimenTablesProvider.SPECIMEN_WRAP_TABLE_NAME); return new TableSelector(specimenTable, filter, null); } @@ -608,13 +609,13 @@ public List getPrimaryTypes(final Container container, @Nullable Si public SpecimenTypeSummary getSpecimenTypeSummary(Container container, @NotNull User user) { UserSchema querySchema = SpecimenQuerySchema.get(StudyService.get().getStudy(container), user); - TableInfo tableInfoSpecimenWrap = querySchema.getTable(SpecimenQuerySchema.SPECIMEN_WRAP_TABLE_NAME); + TableInfo tableInfoSpecimenWrap = querySchema.getTable(SpecimenTablesProvider.SPECIMEN_WRAP_TABLE_NAME); if (null == tableInfoSpecimenWrap) throw new IllegalStateException("SpecimenDetail table not found."); - TableInfo additiveTableInfo = querySchema.getTable(SpecimenQuerySchema.SPECIMEN_ADDITIVE_TABLE_NAME); - TableInfo derivativeTableInfo = querySchema.getTable(SpecimenQuerySchema.SPECIMEN_DERIVATIVE_TABLE_NAME); - TableInfo primaryTypeTableInfo = querySchema.getTable(SpecimenQuerySchema.SPECIMEN_PRIMARY_TYPE_TABLE_NAME); + TableInfo additiveTableInfo = querySchema.getTable(SpecimenTablesProvider.ADDITIVE_TYPE_TABLE_NAME); + TableInfo derivativeTableInfo = querySchema.getTable(SpecimenTablesProvider.DERIVATIVE_TYPE_TABLE_NAME); + TableInfo primaryTypeTableInfo = querySchema.getTable(SpecimenTablesProvider.PRIMARY_TYPE_TABLE_NAME); String tableInfoSelectName = "SpecimenWrap"; // TODO: consider caching diff --git a/specimen/src/org/labkey/specimen/SpecimenModule.java b/specimen/src/org/labkey/specimen/SpecimenModule.java index 6e8ea9da..9dbcf367 100644 --- a/specimen/src/org/labkey/specimen/SpecimenModule.java +++ b/specimen/src/org/labkey/specimen/SpecimenModule.java @@ -48,6 +48,7 @@ import org.labkey.api.specimen.SpecimenQuerySchema; import org.labkey.api.specimen.SpecimenSchema; import org.labkey.api.specimen.SpecimensPage; +import org.labkey.api.specimen.model.SpecimenTablesProvider; import org.labkey.api.study.SpecimenService; import org.labkey.api.study.Study; import org.labkey.api.study.StudyInternalService; @@ -274,15 +275,15 @@ protected void startupAfterSpringConfig(ModuleContext moduleContext) if (settings.isSimple()) { specimenBag.add("simple"); - TableInfo simpleSpecimens = schema.getTable(SpecimenQuerySchema.SIMPLE_SPECIMEN_TABLE_NAME); + TableInfo simpleSpecimens = schema.getTable(SpecimenTablesProvider.SIMPLE_SPECIMEN_TABLE_NAME); specimenBag.add("simpleSpecimens", (int) new TableSelector(simpleSpecimens).getRowCount()); } else { specimenBag.add("advanced"); - TableInfo events = schema.getTable(SpecimenQuerySchema.SPECIMEN_EVENT_TABLE_NAME); - TableInfo vials = schema.getTable(SpecimenQuerySchema.SPECIMEN_DETAIL_TABLE_NAME); - TableInfo specimens = schema.getTable(SpecimenQuerySchema.SPECIMEN_SUMMARY_TABLE_NAME); + TableInfo events = schema.getTable(SpecimenTablesProvider.SPECIMEN_EVENT_TABLE_NAME); + TableInfo vials = schema.getTable(SpecimenTablesProvider.SPECIMEN_DETAIL_TABLE_NAME); + TableInfo specimens = schema.getTable(SpecimenTablesProvider.SPECIMEN_SUMMARY_TABLE_NAME); specimenBag.add("events", (int) new TableSelector(events).getRowCount()); specimenBag.add("vials", (int) new TableSelector(vials).getRowCount()); specimenBag.add("specimens", (int) new TableSelector(specimens).getRowCount()); diff --git a/specimen/src/org/labkey/specimen/SpecimenRequestManager.java b/specimen/src/org/labkey/specimen/SpecimenRequestManager.java index cafe543b..650267ca 100644 --- a/specimen/src/org/labkey/specimen/SpecimenRequestManager.java +++ b/specimen/src/org/labkey/specimen/SpecimenRequestManager.java @@ -38,6 +38,7 @@ import org.labkey.api.specimen.importer.RollupHelper; import org.labkey.api.specimen.importer.RollupInstance; import org.labkey.api.specimen.importer.VialSpecimenRollup; +import org.labkey.api.specimen.model.SpecimenTablesProvider; import org.labkey.api.specimen.security.permissions.RequestSpecimensPermission; import org.labkey.api.study.QueryHelper; import org.labkey.api.study.QueryHelper.StudyCacheCollections; @@ -941,7 +942,7 @@ public Map> load(@NotNull Container c, @Nullable Obj // Basic SQL with joins UserSchema schema = SpecimenQuerySchema.get(study, user); - TableInfo tableInfo = schema.getTable(SpecimenQuerySchema.SPECIMEN_WRAP_TABLE_NAME); + TableInfo tableInfo = schema.getTable(SpecimenTablesProvider.SPECIMEN_WRAP_TABLE_NAME); Map columnMap = queryService.getColumns(tableInfo, fieldKeys); SQLFragment inner = queryService.getSelectSQL(tableInfo, columnMap.values(), null, null, -1, 0, false); diff --git a/specimen/src/org/labkey/specimen/actions/SpecimenController.java b/specimen/src/org/labkey/specimen/actions/SpecimenController.java index 068edfb7..0682600a 100644 --- a/specimen/src/org/labkey/specimen/actions/SpecimenController.java +++ b/specimen/src/org/labkey/specimen/actions/SpecimenController.java @@ -91,6 +91,7 @@ import org.labkey.api.specimen.location.LocationImpl; import org.labkey.api.specimen.location.LocationManager; import org.labkey.api.specimen.model.SpecimenComment; +import org.labkey.api.specimen.model.SpecimenTablesProvider; import org.labkey.api.specimen.security.permissions.EditSpecimenDataPermission; import org.labkey.api.specimen.security.permissions.ManageRequestSettingsPermission; import org.labkey.api.specimen.security.permissions.RequestSpecimensPermission; @@ -746,8 +747,8 @@ private DataRegion createDataRegionForWriters(SpecimenRequest specimenRequest) DataRegion dr = new DataRegion(); Container container = specimenRequest.getContainer(); SpecimenQuerySchema querySchema = SpecimenQuerySchema.get(StudyService.get().getStudy(container), getViewContext().getUser()); - TableInfo table = querySchema.getTable(SpecimenQuerySchema.LOCATION_SPECIMEN_LIST_TABLE_NAME, true); - QueryDefinition queryDef = querySchema.getQueryDefForTable(SpecimenQuerySchema.LOCATION_SPECIMEN_LIST_TABLE_NAME); + TableInfo table = querySchema.getTable(SpecimenTablesProvider.LOCATION_SPECIMEN_LIST_TABLE_NAME, true); + QueryDefinition queryDef = querySchema.getQueryDefForTable(SpecimenTablesProvider.LOCATION_SPECIMEN_LIST_TABLE_NAME); CustomView defaultView = QueryService.get().getCustomView(getViewContext().getUser(), container, getViewContext().getUser(), querySchema.getName(), queryDef.getName(), null); List columns = queryDef.getColumns(defaultView, table); dr.setTable(table); diff --git a/specimen/src/org/labkey/specimen/query/SpecimenQueryView.java b/specimen/src/org/labkey/specimen/query/SpecimenQueryView.java index 19a13a49..1126886f 100644 --- a/specimen/src/org/labkey/specimen/query/SpecimenQueryView.java +++ b/specimen/src/org/labkey/specimen/query/SpecimenQueryView.java @@ -50,6 +50,7 @@ import org.labkey.api.specimen.SpecimenQuerySchema; import org.labkey.api.specimen.SpecimenSchema; import org.labkey.api.specimen.Vial; +import org.labkey.api.specimen.model.SpecimenTablesProvider; import org.labkey.api.specimen.security.permissions.EditSpecimenDataPermission; import org.labkey.api.specimen.security.permissions.RequestSpecimensPermission; import org.labkey.api.study.CohortFilter; @@ -720,7 +721,7 @@ protected static SimpleFilter addPreviouslyRequestedEnrollmentClause(SimpleFilte public static SQLFragment getBaseRequestedEnrollmentSql(Container container, User user, boolean isCompleteRequestsOnly) { UserSchema schema = SpecimenQuerySchema.get(StudyService.get().getStudy(container), user); - TableInfo tableInfoSpecimenDetail = schema.getTable(SpecimenQuerySchema.SPECIMEN_WRAP_TABLE_NAME); + TableInfo tableInfoSpecimenDetail = schema.getTable(SpecimenTablesProvider.SPECIMEN_WRAP_TABLE_NAME); if (null == tableInfoSpecimenDetail) throw new IllegalStateException("SpecimenDetail table not found."); String tableInfoAlias = "Specimen"; diff --git a/specimen/src/org/labkey/specimen/report/SpecimenReportManager.java b/specimen/src/org/labkey/specimen/report/SpecimenReportManager.java index 599a7cce..166d826d 100644 --- a/specimen/src/org/labkey/specimen/report/SpecimenReportManager.java +++ b/specimen/src/org/labkey/specimen/report/SpecimenReportManager.java @@ -17,6 +17,7 @@ import org.labkey.api.security.User; import org.labkey.api.specimen.SpecimenQuerySchema; import org.labkey.api.specimen.SpecimenSchema; +import org.labkey.api.specimen.model.SpecimenTablesProvider; import org.labkey.api.study.CohortFilter; import org.labkey.api.study.StudyService; import org.labkey.api.view.ActionURL; @@ -50,7 +51,7 @@ private SpecimenDetailQueryHelper getSpecimenDetailQueryHelper(Container contain SpecimenTypeLevel level) { UserSchema schema = SpecimenQuerySchema.get(StudyService.get().getStudy(container), user); - TableInfo tinfo = schema.getTable(SpecimenQuerySchema.SPECIMEN_DETAIL_TABLE_NAME); + TableInfo tinfo = schema.getTable(SpecimenTablesProvider.SPECIMEN_DETAIL_TABLE_NAME); Map aliasToTypeProperty = new LinkedHashMap<>(); diff --git a/specimen/src/org/labkey/specimen/writer/SpecimenArchiveWriter.java b/specimen/src/org/labkey/specimen/writer/SpecimenArchiveWriter.java index 398784a5..e0f3f33e 100644 --- a/specimen/src/org/labkey/specimen/writer/SpecimenArchiveWriter.java +++ b/specimen/src/org/labkey/specimen/writer/SpecimenArchiveWriter.java @@ -87,17 +87,17 @@ public void write(Study study, SimpleStudyExportContext ctx, VirtualFile root) t TablesType tablesXml = tablesDoc.addNewTables(); Map specimenTables = new HashMap<>(); - TableInfo eventTable = tablesProvider.getTableInfoIfExists(SpecimenTablesProvider.SPECIMENEVENT_TABLENAME); + TableInfo eventTable = tablesProvider.getTableInfoIfExists(SpecimenTablesProvider.SPECIMEN_EVENT_TABLE_NAME); if (eventTable != null) - specimenTables.put(SpecimenTablesProvider.SPECIMENEVENT_TABLENAME, eventTable); + specimenTables.put(SpecimenTablesProvider.SPECIMEN_EVENT_TABLE_NAME, eventTable); - TableInfo specimenTable = tablesProvider.getTableInfoIfExists(SpecimenTablesProvider.SPECIMEN_TABLENAME); + TableInfo specimenTable = tablesProvider.getTableInfoIfExists(SpecimenTablesProvider.SPECIMEN_TABLE_NAME); if (specimenTable != null) - specimenTables.put(SpecimenTablesProvider.SPECIMEN_TABLENAME, specimenTable); + specimenTables.put(SpecimenTablesProvider.SPECIMEN_TABLE_NAME, specimenTable); - TableInfo vialTable = tablesProvider.getTableInfoIfExists(SpecimenTablesProvider.VIAL_TABLENAME); + TableInfo vialTable = tablesProvider.getTableInfoIfExists(SpecimenTablesProvider.VIAL_TABLE_NAME); if (vialTable != null) - specimenTables.put(SpecimenTablesProvider.VIAL_TABLENAME, vialTable); + specimenTables.put(SpecimenTablesProvider.VIAL_TABLE_NAME, vialTable); for (Map.Entry entry : specimenTables.entrySet()) { From 4e7cc1c43ae15dd58ffb945d8e248e2c4e910e73 Mon Sep 17 00:00:00 2001 From: labkey-matthewb Date: Wed, 13 Aug 2025 17:11:56 -0700 Subject: [PATCH 450/494] Support BIGINT Primary Keys Original PR: https://github.com/LabKey/platform/pull/6911 --- specimen/src/org/labkey/specimen/SpecimenManager.java | 3 ++- specimen/src/org/labkey/specimen/SpecimenModule.java | 2 +- .../src/org/labkey/specimen/SpecimenRequestManager.java | 8 +++++--- .../org/labkey/specimen/actions/ManageRequestBean.java | 4 ++-- .../org/labkey/specimen/actions/SpecimenController.java | 7 ++++--- .../org/labkey/specimen/importer/SpecimenImporter.java | 3 ++- .../labkey/specimen/query/BaseSpecimenPivotTable.java | 9 +++++---- .../org/labkey/specimen/query/SpecimenUpdateService.java | 5 +++-- .../org/labkey/specimen/report/SpecimenVisitReport.java | 5 +++-- .../report/request/RequestParticipantReportFactory.java | 3 ++- .../specimentype/TypeParticipantReportFactory.java | 3 ++- 11 files changed, 31 insertions(+), 21 deletions(-) diff --git a/specimen/src/org/labkey/specimen/SpecimenManager.java b/specimen/src/org/labkey/specimen/SpecimenManager.java index 31025d82..8b1cef9d 100644 --- a/specimen/src/org/labkey/specimen/SpecimenManager.java +++ b/specimen/src/org/labkey/specimen/SpecimenManager.java @@ -3,6 +3,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.labkey.api.audit.AuditLogService; +import org.labkey.api.collections.LongArrayList; import org.labkey.api.data.ColumnInfo; import org.labkey.api.data.Container; import org.labkey.api.data.DbScope; @@ -489,7 +490,7 @@ public List getVials(Container container, User user, Set vialRowIds) List vials = getVials(container, user, filter); if (vials.size() != vialRowIds.size()) { - List unmatchedRowIds = new ArrayList<>(vialRowIds); + List unmatchedRowIds = new LongArrayList(vialRowIds); for (Vial vial : vials) { unmatchedRowIds.remove(vial.getRowId()); diff --git a/specimen/src/org/labkey/specimen/SpecimenModule.java b/specimen/src/org/labkey/specimen/SpecimenModule.java index 9dbcf367..afdde6c7 100644 --- a/specimen/src/org/labkey/specimen/SpecimenModule.java +++ b/specimen/src/org/labkey/specimen/SpecimenModule.java @@ -303,7 +303,7 @@ protected void startupAfterSpringConfig(ModuleContext moduleContext) }); Map specimensMap = specimenBag.uniqueSet().stream().collect(Collectors.toMap(s -> s, specimenBag::getCount)); - Map requestsMap = new SqlSelector(SpecimenSchema.get().getSchema(), new SQLFragment("SELECT Label, COUNT(*) FROM study.SampleRequest INNER JOIN study.SampleRequestStatus srs ON StatusId = srs.RowId GROUP BY Label")).getValueMap(); + Map requestsMap = new SqlSelector(SpecimenSchema.get().getSchema(), new SQLFragment("SELECT Label, COUNT(*) FROM study.SampleRequest INNER JOIN study.SampleRequestStatus srs ON StatusId = srs.RowId GROUP BY Label")).getValueMap(String.class); requestsMap.put("enabled", requestsEnabled); specimensMap.put("requests", requestsMap); specimensMap.put("hasLocations", hasLocations.intValue()); diff --git a/specimen/src/org/labkey/specimen/SpecimenRequestManager.java b/specimen/src/org/labkey/specimen/SpecimenRequestManager.java index 650267ca..dc68ce89 100644 --- a/specimen/src/org/labkey/specimen/SpecimenRequestManager.java +++ b/specimen/src/org/labkey/specimen/SpecimenRequestManager.java @@ -10,6 +10,8 @@ import org.labkey.api.cache.BlockingCache; import org.labkey.api.cache.CacheLoader; import org.labkey.api.cache.CacheManager; +import org.labkey.api.collections.IntHashMap; +import org.labkey.api.collections.LongArrayList; import org.labkey.api.data.ColumnInfo; import org.labkey.api.data.Container; import org.labkey.api.data.DatabaseCache; @@ -250,7 +252,7 @@ public Map getDefaultSiteValues(Container container) throws Vali return _locationToDefaultValue; String defaultObjectLsid = getRequestInputDefaultObjectLsid(container); String setItemLsid = ensureOntologyManagerSetItem(container, defaultObjectLsid, getTitle()); - Map locationToValue = new HashMap<>(); + Map locationToValue = new IntHashMap<>(); Map defaultValueProperties = OntologyManager.getPropertyObjects(container, setItemLsid); if (defaultValueProperties != null) @@ -351,7 +353,7 @@ private static String ensureOntologyManagerSetItem(Container container, String l { try (var ignore = SpringActionController.ignoreSqlUpdates()) { - Integer listParentObjectId = OntologyManager.ensureObject(container, lsidBase); + Long listParentObjectId = OntologyManager.ensureObject(container, lsidBase); String listItemReferenceLsidPrefix = lsidBase + "#objectResource."; String listItemObjectLsid = lsidBase + "#" + uniqueItemId; String listItemPropertyReferenceLsid = listItemReferenceLsidPrefix + uniqueItemId; @@ -1114,7 +1116,7 @@ public void deleteRequest(User user, SpecimenRequest request) throws Requestabil try (DbScope.Transaction transaction = scope.ensureTransaction()) { List vials = request.getVials(); - List specimenIds = new ArrayList<>(vials.size()); + List specimenIds = new LongArrayList(vials.size()); for (Vial vial : vials) specimenIds.add(vial.getRowId()); diff --git a/specimen/src/org/labkey/specimen/actions/ManageRequestBean.java b/specimen/src/org/labkey/specimen/actions/ManageRequestBean.java index 759d4907..d604e866 100644 --- a/specimen/src/org/labkey/specimen/actions/ManageRequestBean.java +++ b/specimen/src/org/labkey/specimen/actions/ManageRequestBean.java @@ -1,5 +1,6 @@ package org.labkey.specimen.actions; +import org.labkey.api.collections.IntHashMap; import org.labkey.api.data.ActionButton; import org.labkey.api.data.Container; import org.labkey.api.data.MenuButton; @@ -24,7 +25,6 @@ import org.labkey.specimen.security.permissions.ManageRequestsPermission; import java.util.ArrayList; -import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -102,7 +102,7 @@ public List getPossibleNotifications(SpecimenRequ addIfNotPresent(requirement.getActor(), requirement.getLocation(), possibleNotifications); // allow notification of all site-based actors at the destination site, and all study-wide actors: - Map relevantSites = new HashMap<>(); + Map relevantSites = new IntHashMap<>(); if (specimenRequest.getDestinationSiteId() == null) { throw new IllegalStateException("Request " + specimenRequest.getRowId() + " in folder " + diff --git a/specimen/src/org/labkey/specimen/actions/SpecimenController.java b/specimen/src/org/labkey/specimen/actions/SpecimenController.java index 0682600a..d3d18097 100644 --- a/specimen/src/org/labkey/specimen/actions/SpecimenController.java +++ b/specimen/src/org/labkey/specimen/actions/SpecimenController.java @@ -34,6 +34,7 @@ import org.labkey.api.attachments.BaseDownloadAction; import org.labkey.api.attachments.ByteArrayAttachmentFile; import org.labkey.api.audit.TransactionAuditProvider; +import org.labkey.api.collections.IntHashMap; import org.labkey.api.data.ActionButton; import org.labkey.api.data.BaseColumnInfo; import org.labkey.api.data.BeanViewForm; @@ -621,7 +622,7 @@ private static List getPossibleNotifications(Spec addIfNotPresent(requirement.getActor(), requirement.getLocation(), possibleNotifications); // allow notification of all site-based actors at the destination site, and all study-wide actors: - Map relevantSites = new HashMap<>(); + Map relevantSites = new IntHashMap<>(); if (specimenRequest.getDestinationSiteId() == null) { throw new IllegalStateException("Request " + specimenRequest.getRowId() + " in folder " + @@ -2761,7 +2762,7 @@ public void setNewPerSite(boolean newPerSite) private Map getIdToRequestActorMap(Container container) { SpecimenRequestActor[] actors = SpecimenRequestRequirementProvider.get().getActors(container); - Map idToStatus = new HashMap<>(); + Map idToStatus = new IntHashMap<>(); for (SpecimenRequestActor actor : actors) idToStatus.put(actor.getRowId(), actor); return idToStatus; @@ -2924,7 +2925,7 @@ private void updateRequestStatusOrder(Container container, int[] rowIds, boolean private Map getIdToRequestStatusMap(Container container) { Collection statuses = SpecimenRequestManager.get().getRequestStatuses(container, getUser()); - Map idToStatus = new HashMap<>(); + Map idToStatus = new IntHashMap<>(); for (SpecimenRequestStatus status : statuses) idToStatus.put(status.getRowId(), status); return idToStatus; diff --git a/specimen/src/org/labkey/specimen/importer/SpecimenImporter.java b/specimen/src/org/labkey/specimen/importer/SpecimenImporter.java index 80b69f1f..742bd917 100644 --- a/specimen/src/org/labkey/specimen/importer/SpecimenImporter.java +++ b/specimen/src/org/labkey/specimen/importer/SpecimenImporter.java @@ -27,6 +27,7 @@ import org.junit.Test; import org.labkey.api.collections.CaseInsensitiveHashMap; import org.labkey.api.collections.CaseInsensitiveHashSet; +import org.labkey.api.collections.IntHashMap; import org.labkey.api.data.BaseColumnInfo; import org.labkey.api.data.ColumnInfo; import org.labkey.api.data.Container; @@ -813,7 +814,7 @@ private void updateCalculatedSpecimenData(final boolean merge, final boolean edi _iTimer.setPhase(ImportPhases.VialUpdatePreLoopPrep); // clear caches before determining current sites: SpecimenRequestManager.get().clearCaches(getContainer()); - final Map siteMap = new HashMap<>(); + final Map siteMap = new IntHashMap<>(); TableInfo vialTable = getTableInfoVial(); SQLFragment vialPropertiesSql = new SQLFragment("UPDATE ").append(vialTable.getSelectName()) diff --git a/specimen/src/org/labkey/specimen/query/BaseSpecimenPivotTable.java b/specimen/src/org/labkey/specimen/query/BaseSpecimenPivotTable.java index 099b4542..03455d23 100644 --- a/specimen/src/org/labkey/specimen/query/BaseSpecimenPivotTable.java +++ b/specimen/src/org/labkey/specimen/query/BaseSpecimenPivotTable.java @@ -19,6 +19,7 @@ import org.jetbrains.annotations.NotNull; import org.labkey.api.action.SpringActionController; import org.labkey.api.collections.CaseInsensitiveHashSet; +import org.labkey.api.collections.IntHashMap; import org.labkey.api.data.ColumnInfo; import org.labkey.api.data.Container; import org.labkey.api.data.ContainerFilter; @@ -250,7 +251,7 @@ private void ensureUniqueWrappedName(StringBuilder name) protected Map getPrimaryTypeMap(Container container) { - Map typeMap = new HashMap<>(); + Map typeMap = new IntHashMap<>(); LegalCaseInsensitiveMap legalMap = new LegalCaseInsensitiveMap(); SpecimenTypeSummary summary = SpecimenManager.get().getSpecimenTypeSummary(container, getUserSchema().getUser()); List primaryTypes = summary.getPrimaryTypes(); @@ -274,7 +275,7 @@ protected Map getPrimaryTypeMap(Container container) public Map getAllPrimaryTypesMap(Container container) { - Map typeMap = new HashMap<>(); + Map typeMap = new IntHashMap<>(); LegalCaseInsensitiveMap legalMap = new LegalCaseInsensitiveMap(); List primaryTypes = SpecimenManager.get().getPrimaryTypes(container); @@ -293,7 +294,7 @@ public Map getAllPrimaryTypesMap(Container container) protected Map getDerivativeTypeMap(Container container) { - Map typeMap = new HashMap<>(); + Map typeMap = new IntHashMap<>(); LegalCaseInsensitiveMap legalMap = new LegalCaseInsensitiveMap(); SpecimenTypeSummary summary = SpecimenManager.get().getSpecimenTypeSummary(container, getUserSchema().getUser()); List types = summary.getDerivatives(); @@ -315,7 +316,7 @@ protected Map getDerivativeTypeMap(Container container) protected Map getSiteMap(Container container) { - Map siteMap = new HashMap<>(); + Map siteMap = new IntHashMap<>(); LegalCaseInsensitiveMap legalMap = new LegalCaseInsensitiveMap(); List locations = LocationManager.get().getLocations(container); diff --git a/specimen/src/org/labkey/specimen/query/SpecimenUpdateService.java b/specimen/src/org/labkey/specimen/query/SpecimenUpdateService.java index 99192ede..a95ad078 100644 --- a/specimen/src/org/labkey/specimen/query/SpecimenUpdateService.java +++ b/specimen/src/org/labkey/specimen/query/SpecimenUpdateService.java @@ -19,6 +19,7 @@ import org.apache.logging.log4j.Logger; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.labkey.api.collections.LongHashMap; import org.labkey.api.data.ColumnInfo; import org.labkey.api.data.Container; import org.labkey.api.data.SQLFragment; @@ -334,7 +335,7 @@ public List> updateRows(User user, Container container, List getQueryTable().fireBatchTrigger(container, user, TableInfo.TriggerType.UPDATE, true, errors, extraScriptContext); Set rowIds = new HashSet<>(rows.size()); - Map> uniqueRows = new HashMap<>(rows.size()); + Map> uniqueRows = new LongHashMap<>(rows.size()); for (int i = 0; i < rows.size(); i++) { Map row = rows.get(i); @@ -351,7 +352,7 @@ public List> updateRows(User user, Container container, List rowIds.add(rowId); uniqueRows.put(rowId, row); } - Map vials = new HashMap<>(); + Map vials = new LongHashMap<>(); for (Vial vial : SpecimenManager.get().getVials(container, user, rowIds)) { vials.put(vial.getRowId(), vial); diff --git a/specimen/src/org/labkey/specimen/report/SpecimenVisitReport.java b/specimen/src/org/labkey/specimen/report/SpecimenVisitReport.java index 9357a5a4..d6c08bee 100644 --- a/specimen/src/org/labkey/specimen/report/SpecimenVisitReport.java +++ b/specimen/src/org/labkey/specimen/report/SpecimenVisitReport.java @@ -15,6 +15,7 @@ */ package org.labkey.specimen.report; +import org.labkey.api.collections.IntHashMap; import org.labkey.api.data.Container; import org.labkey.api.data.SimpleFilter; import org.labkey.api.query.CustomView; @@ -54,7 +55,7 @@ public abstract class SpecimenVisitReport _nonEmptyColumns = new HashMap<>(); + private final Map _nonEmptyColumns = new IntHashMap<>(); protected final Container _container; protected final SimpleFilter _filter; @@ -254,7 +255,7 @@ private static SpecimenReportTitle[] getSpecimenReportTitle(String[] stringHiera public class Row { private final SpecimenReportTitle[] _titleHierarchy; - private final Map _visitData = new HashMap<>(); + private final Map _visitData = new IntHashMap<>(); public Row(String[] stringHierarchy) { diff --git a/specimen/src/org/labkey/specimen/report/request/RequestParticipantReportFactory.java b/specimen/src/org/labkey/specimen/report/request/RequestParticipantReportFactory.java index 68cc1e76..a8c6f66e 100644 --- a/specimen/src/org/labkey/specimen/report/request/RequestParticipantReportFactory.java +++ b/specimen/src/org/labkey/specimen/report/request/RequestParticipantReportFactory.java @@ -15,6 +15,7 @@ */ package org.labkey.specimen.report.request; +import org.labkey.api.collections.IntHashMap; import org.labkey.api.data.SimpleFilter; import org.labkey.api.query.FieldKey; import org.labkey.api.security.User; @@ -113,7 +114,7 @@ protected List createReports() } List reports = new ArrayList<>(); - Map> visitListCache = new HashMap<>(); // cohort rowId -> visit + Map> visitListCache = new IntHashMap<>(); // cohort rowId -> visit boolean showCohorts = StudyService.get().showCohorts(getContainer(), getUser()); Collection allVisits = null; Study study = StudyService.get().getStudy(getContainer()); diff --git a/specimen/src/org/labkey/specimen/report/specimentype/TypeParticipantReportFactory.java b/specimen/src/org/labkey/specimen/report/specimentype/TypeParticipantReportFactory.java index 4105d949..eef11f44 100644 --- a/specimen/src/org/labkey/specimen/report/specimentype/TypeParticipantReportFactory.java +++ b/specimen/src/org/labkey/specimen/report/specimentype/TypeParticipantReportFactory.java @@ -15,6 +15,7 @@ */ package org.labkey.specimen.report.specimentype; +import org.labkey.api.collections.IntHashMap; import org.labkey.api.data.SimpleFilter; import org.labkey.api.query.FieldKey; import org.labkey.api.security.User; @@ -102,7 +103,7 @@ protected List createReports() return Collections.emptyList(); } List reports = new ArrayList<>(); - Map> visitListCache = new HashMap<>(); // cohort rowId -> visits + Map> visitListCache = new IntHashMap<>(); // cohort rowId -> visits boolean showCohorts = StudyService.get().showCohorts(getContainer(), getUser()); Collection allVisits = null; Study study = StudyService.get().getStudy(getContainer()); From b1c64d9eec3d3556c74b417aa3433be7adf2a663 Mon Sep 17 00:00:00 2001 From: labkey-matthewb Date: Tue, 19 Aug 2025 10:05:55 -0700 Subject: [PATCH 451/494] IntegerUtils class Original PR: https://github.com/LabKey/platform/pull/6933 --- .../src/org/labkey/specimen/SpecimenRequestManager.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/specimen/src/org/labkey/specimen/SpecimenRequestManager.java b/specimen/src/org/labkey/specimen/SpecimenRequestManager.java index dc68ce89..9a62c131 100644 --- a/specimen/src/org/labkey/specimen/SpecimenRequestManager.java +++ b/specimen/src/org/labkey/specimen/SpecimenRequestManager.java @@ -77,6 +77,8 @@ import java.util.Set; import java.util.concurrent.locks.ReentrantLock; +import static org.labkey.api.util.IntegerUtils.asLongElseNull; + public class SpecimenRequestManager { private static final SpecimenRequestManager INSTANCE = new SpecimenRequestManager(); @@ -992,10 +994,8 @@ public Map> load(@NotNull Container c, @Nullable Obj Map rowMap = resultSet.getRowMap(); long count = 0; Object countObject = rowMap.get(sampleCountName); - if (countObject instanceof Long) - count = (Long) countObject; - else if (countObject instanceof Integer) - count = (Integer) countObject; + if (asLongElseNull(countObject) instanceof Long l) + count = l; Map currentGroupedResultsMap = groupedResultsMap; From 9e5cedf0f2fde45f7baf1d5b58cc6003a6c9ad13 Mon Sep 17 00:00:00 2001 From: Josh Eckels Date: Fri, 29 Aug 2025 10:06:02 -0700 Subject: [PATCH 452/494] Consolidate XML parsing initialization, remove some casts Original PR: https://github.com/LabKey/platform/pull/6968 --- specimen/src/org/labkey/specimen/importer/SpecimenImporter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specimen/src/org/labkey/specimen/importer/SpecimenImporter.java b/specimen/src/org/labkey/specimen/importer/SpecimenImporter.java index 742bd917..3379c3f4 100644 --- a/specimen/src/org/labkey/specimen/importer/SpecimenImporter.java +++ b/specimen/src/org/labkey/specimen/importer/SpecimenImporter.java @@ -1370,7 +1370,7 @@ private void populateMaterials(SpecimenLoadInfo info, boolean merge) /* HACK, we don't do this anywhere else, but we need to get a block of continuous IDS to make this work */ // row_number() starts at 1 so subtract 1 - long start = DbSequenceManager.reserveSequentialBlock(seq, (int)count); + long start = DbSequenceManager.reserveSequentialBlock(seq, count); start -= 1; SQLFragment insertMaterialFragment = new SQLFragment(insertMaterialSQL, start, start, info.getContainer().getId(), cpasType, createdTimestamp); From 55a63b83727e1a62dd3a414fb39a2530bca8a15e Mon Sep 17 00:00:00 2001 From: Josh Eckels Date: Sat, 30 Aug 2025 17:56:28 -0700 Subject: [PATCH 453/494] Backport: Consolidate XML parsing initialization, remove some casts Original PR: https://github.com/LabKey/platform/pull/6976 --- specimen/src/org/labkey/specimen/importer/SpecimenImporter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specimen/src/org/labkey/specimen/importer/SpecimenImporter.java b/specimen/src/org/labkey/specimen/importer/SpecimenImporter.java index 80b69f1f..553ab45e 100644 --- a/specimen/src/org/labkey/specimen/importer/SpecimenImporter.java +++ b/specimen/src/org/labkey/specimen/importer/SpecimenImporter.java @@ -1369,7 +1369,7 @@ private void populateMaterials(SpecimenLoadInfo info, boolean merge) /* HACK, we don't do this anywhere else, but we need to get a block of continuous IDS to make this work */ // row_number() starts at 1 so subtract 1 - long start = DbSequenceManager.reserveSequentialBlock(seq, (int)count); + long start = DbSequenceManager.reserveSequentialBlock(seq, count); start -= 1; SQLFragment insertMaterialFragment = new SQLFragment(insertMaterialSQL, start, start, info.getContainer().getId(), cpasType, createdTimestamp); From 404a00253588fe786d6145e17a9acf94ffa826b0 Mon Sep 17 00:00:00 2001 From: Josh Eckels Date: Mon, 1 Sep 2025 16:55:13 -0700 Subject: [PATCH 454/494] Backport: Consolidate XML parsing initialization, remove some casts Original PR: https://github.com/LabKey/platform/pull/6980 * Backport: Consolidate XML parsing initialization, remove some casts * Matching version for Commons CSV --- specimen/src/org/labkey/specimen/importer/SpecimenImporter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specimen/src/org/labkey/specimen/importer/SpecimenImporter.java b/specimen/src/org/labkey/specimen/importer/SpecimenImporter.java index 21bb1fa1..599ba7ac 100644 --- a/specimen/src/org/labkey/specimen/importer/SpecimenImporter.java +++ b/specimen/src/org/labkey/specimen/importer/SpecimenImporter.java @@ -1383,7 +1383,7 @@ private void populateMaterials(SpecimenLoadInfo info, boolean merge) /* HACK, we don't do this anywhere else, but we need to get a block of continuous IDS to make this work */ // row_number() starts at 1 so subtract 1 - long start = DbSequenceManager.reserveSequentialBlock(seq, (int)count); + long start = DbSequenceManager.reserveSequentialBlock(seq, count); start -= 1; SQLFragment insertMaterialFragment = new SQLFragment(insertMaterialSQL, start, start, info.getContainer().getId(), cpasType, createdTimestamp); From 59b5a21b88569cf8a761711b0f072fdbc9429185 Mon Sep 17 00:00:00 2001 From: Josh Eckels Date: Mon, 1 Sep 2025 16:55:24 -0700 Subject: [PATCH 455/494] Backport: Consolidate XML parsing initialization, remove some casts Original PR: https://github.com/LabKey/platform/pull/6976 Original PR: https://github.com/LabKey/platform/pull/6984 --- specimen/src/org/labkey/specimen/importer/SpecimenImporter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specimen/src/org/labkey/specimen/importer/SpecimenImporter.java b/specimen/src/org/labkey/specimen/importer/SpecimenImporter.java index 80b69f1f..553ab45e 100644 --- a/specimen/src/org/labkey/specimen/importer/SpecimenImporter.java +++ b/specimen/src/org/labkey/specimen/importer/SpecimenImporter.java @@ -1369,7 +1369,7 @@ private void populateMaterials(SpecimenLoadInfo info, boolean merge) /* HACK, we don't do this anywhere else, but we need to get a block of continuous IDS to make this work */ // row_number() starts at 1 so subtract 1 - long start = DbSequenceManager.reserveSequentialBlock(seq, (int)count); + long start = DbSequenceManager.reserveSequentialBlock(seq, count); start -= 1; SQLFragment insertMaterialFragment = new SQLFragment(insertMaterialSQL, start, start, info.getContainer().getId(), cpasType, createdTimestamp); From d22df1e4770974bb7eca218f892a0e4b709fb5e9 Mon Sep 17 00:00:00 2001 From: Josh Eckels Date: Fri, 5 Sep 2025 14:30:04 -0700 Subject: [PATCH 456/494] Attempt to improve code analysis w/ Pair->record Original PR: https://github.com/LabKey/platform/pull/7015 * Attempt to improve code analysis w/ Pair->record * Fix bootstrapping --- .../org/labkey/specimen/actions/ParticipantCommentAction.java | 3 ++- .../src/org/labkey/specimen/importer/SpecimenImporter.java | 4 ++-- specimen/src/org/labkey/specimen/view/specimenVisitReport.jsp | 4 ++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/specimen/src/org/labkey/specimen/actions/ParticipantCommentAction.java b/specimen/src/org/labkey/specimen/actions/ParticipantCommentAction.java index a1380811..17c2438a 100644 --- a/specimen/src/org/labkey/specimen/actions/ParticipantCommentAction.java +++ b/specimen/src/org/labkey/specimen/actions/ParticipantCommentAction.java @@ -17,6 +17,7 @@ package org.labkey.specimen.actions; import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.Strings; import org.labkey.api.data.ColumnInfo; import org.labkey.api.data.Container; import org.labkey.api.query.QueryUpdateForm; @@ -148,7 +149,7 @@ protected DataView createNewView(final ParticipantCommentForm form, QueryUpdateF String oldComment = String.valueOf(valueMap.get(commentProperty)); String newComment = form.getComment(); - if (!StringUtils.isBlank(oldComment) && !StringUtils.equals(oldComment, form.getComment())) + if (!StringUtils.isBlank(oldComment) && !Strings.CS.equals(oldComment, form.getComment())) newComment = oldComment + " : " + form.getComment(); valueMap.put(commentProperty, newComment); } diff --git a/specimen/src/org/labkey/specimen/importer/SpecimenImporter.java b/specimen/src/org/labkey/specimen/importer/SpecimenImporter.java index 3379c3f4..2098172f 100644 --- a/specimen/src/org/labkey/specimen/importer/SpecimenImporter.java +++ b/specimen/src/org/labkey/specimen/importer/SpecimenImporter.java @@ -2274,9 +2274,9 @@ public Object getValue(Map row) throws ValidationException SpecimenColumn _participantIdCol = null; for (SpecimenColumn sc : _specimenColumns) { - if (StringUtils.equals("VisitValue", sc.getDbColumnName())) + if ("VisitValue".equals(sc.getDbColumnName())) _visitCol = sc; - else if (StringUtils.equals("Ptid", sc.getDbColumnName())) + else if ("Ptid".equals(sc.getDbColumnName())) _participantIdCol = sc; } diff --git a/specimen/src/org/labkey/specimen/view/specimenVisitReport.jsp b/specimen/src/org/labkey/specimen/view/specimenVisitReport.jsp index 2771142a..69f19de0 100644 --- a/specimen/src/org/labkey/specimen/view/specimenVisitReport.jsp +++ b/specimen/src/org/labkey/specimen/view/specimenVisitReport.jsp @@ -15,7 +15,6 @@ * limitations under the License. */ %> -<%@ page import="org.apache.commons.lang3.StringUtils"%> <%@ page import="org.labkey.api.study.SpecimenService" %> <%@ page import="org.labkey.api.study.Study" %> <%@ page import="org.labkey.api.study.StudyService" %> @@ -28,6 +27,7 @@ <%@ page import="org.labkey.specimen.report.SpecimenVisitReportParameters" %> <%@ page import="java.util.Collection" %> <%@ page import="java.util.List" %> +<%@ page import="org.apache.commons.lang3.Strings" %> <%@ page extends="org.labkey.api.jsp.JspBase" %> <% JspView me = HttpView.currentView(); @@ -108,7 +108,7 @@ The request has produced no records. { for (int col=0 ; col < width ; col++) { - if (!StringUtils.equals(rowtitles[row][col].first.getValue(), rowtitles[row-1][col].first.getValue())) + if (!Strings.CS.equals(rowtitles[row][col].first.getValue(), rowtitles[row-1][col].first.getValue())) break; rowtitles[row-1][col].second += rowtitles[row][col].second; rowtitles[row][col].second = 0; From 4f87cd05b937acc10fa644a2c8cd85ced1fae24c Mon Sep 17 00:00:00 2001 From: Trey Chadick Date: Thu, 11 Sep 2025 13:52:10 -0700 Subject: [PATCH 457/494] Update tests to stop using controller-first URLs Original PR: https://github.com/LabKey/testAutomation/pull/2681 - Update many tests to use WebTestHelper to build URLs - Only log the first time a particular controller-first URL is found --- src/org/labkey/test/tests/SpecimenGridExportTest.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/org/labkey/test/tests/SpecimenGridExportTest.java b/src/org/labkey/test/tests/SpecimenGridExportTest.java index 0ee1cf70..f646559a 100644 --- a/src/org/labkey/test/tests/SpecimenGridExportTest.java +++ b/src/org/labkey/test/tests/SpecimenGridExportTest.java @@ -21,12 +21,14 @@ import org.labkey.test.BaseWebDriverTest; import org.labkey.test.Locator; import org.labkey.test.TestTimeoutException; +import org.labkey.test.WebTestHelper; import org.labkey.test.categories.Daily; import org.labkey.test.categories.Specimen; import org.labkey.test.util.AbstractDataRegionExportOrSignHelper.ColumnHeaderType; import java.util.Arrays; import java.util.List; +import java.util.Map; /** * Test exporting rows from a specimen grid (not folder/study specimen export.) @@ -170,7 +172,7 @@ protected String getDataRegionId() @Override protected void goToDataRegionPage() { - beginAt("/" + getProjectName() + "/specimen-specimens.view?showVials=false"); + beginAt(WebTestHelper.buildURL("specimens", getProjectName(), "specimens", Map.of("showVials", false))); } @Override From b677f6b402d2ac86fe532f9d3fd9fb300a953034 Mon Sep 17 00:00:00 2001 From: Trey Chadick Date: Fri, 12 Sep 2025 14:04:44 -0700 Subject: [PATCH 458/494] Fix the specimen controller name in SpecimenGridExportTest Original PR: https://github.com/LabKey/testAutomation/pull/2686 --- src/org/labkey/test/tests/SpecimenGridExportTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/org/labkey/test/tests/SpecimenGridExportTest.java b/src/org/labkey/test/tests/SpecimenGridExportTest.java index f646559a..89d7aab9 100644 --- a/src/org/labkey/test/tests/SpecimenGridExportTest.java +++ b/src/org/labkey/test/tests/SpecimenGridExportTest.java @@ -172,7 +172,7 @@ protected String getDataRegionId() @Override protected void goToDataRegionPage() { - beginAt(WebTestHelper.buildURL("specimens", getProjectName(), "specimens", Map.of("showVials", false))); + beginAt(WebTestHelper.buildURL("specimen", getProjectName(), "specimens", Map.of("showVials", false))); } @Override From 59659a48b7fef889fc547051957428753ce2b25f Mon Sep 17 00:00:00 2001 From: Susan Hert Date: Mon, 15 Sep 2025 11:54:01 -0700 Subject: [PATCH 459/494] Update Amounts and Units for better sorting and filtering behavior Original PR: https://github.com/LabKey/platform/pull/6994 - Upgrade script to convert existing amounts and units to base units, and drop any units that don't have values - Remove Measurement class in favor of Quantity and Units classes - Update detailed audit log to handle provided values that are converted before being stored - Update aliquot rollup logic to store values in base units - Remove recompute of rollup values when display unit is changed - Add SampleTypeAmountDisplayColumn and SampleTyepUnitDisplayColumn to display amount and units using chosen display units --- .../src/org/labkey/specimen/query/SpecimenUpdateService.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/specimen/src/org/labkey/specimen/query/SpecimenUpdateService.java b/specimen/src/org/labkey/specimen/query/SpecimenUpdateService.java index a95ad078..e9d88406 100644 --- a/specimen/src/org/labkey/specimen/query/SpecimenUpdateService.java +++ b/specimen/src/org/labkey/specimen/query/SpecimenUpdateService.java @@ -141,7 +141,7 @@ public List> deleteRows(User user, Container container, List getQueryTable().fireBatchTrigger(container, user, TableInfo.TriggerType.DELETE, false, errors, extraScriptContext); - addAuditEvent(user, container, QueryService.AuditAction.DELETE, configParameters, null, null); + addAuditEvent(user, container, QueryService.AuditAction.DELETE, configParameters, null, null, null); return new ArrayList<>(); } @@ -408,7 +408,7 @@ public List> updateRows(User user, Container container, List getQueryTable().fireBatchTrigger(container, user, TableInfo.TriggerType.UPDATE, false, errors, extraScriptContext); - addAuditEvent(user, container, QueryService.AuditAction.UPDATE, configParameters, rows, null); + addAuditEvent(user, container, QueryService.AuditAction.UPDATE, configParameters, rows, null, null); return getRows(user, container, newKeys); } From a59c9f0a9691739457ae64aefb2c2ba0b16e7754 Mon Sep 17 00:00:00 2001 From: Nick Kerr Date: Mon, 15 Sep 2025 17:25:45 -0700 Subject: [PATCH 460/494] Test updates to escape special characters in form field names Original PR: https://github.com/LabKey/testAutomation/pull/2683 --- src/org/labkey/test/tests/AliquotTest.java | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/org/labkey/test/tests/AliquotTest.java b/src/org/labkey/test/tests/AliquotTest.java index 0028f416..3a95f168 100644 --- a/src/org/labkey/test/tests/AliquotTest.java +++ b/src/org/labkey/test/tests/AliquotTest.java @@ -24,6 +24,7 @@ import org.labkey.test.categories.Daily; import org.labkey.test.categories.Specimen; import org.labkey.test.components.html.BootstrapMenu; +import org.labkey.test.components.html.OptionSelect; import org.labkey.test.pages.ImportDataPage; import org.labkey.test.util.DataRegionTable; import org.labkey.test.util.LogMethod; @@ -274,12 +275,12 @@ private void verifyInsertingSpecimens() // verify insert new here DataRegionTable detail = new DataRegionTable("SpecimenDetail", this); - detail.clickInsertNewRow(); - setFormElement(Locator.xpath("//input[@name='quf_GlobalUniqueId']"), "Global"); - setFormElement(Locator.xpath("//input[@name='quf_VisitDescription']"), "NewVisit"); - setFormElement(Locator.xpath("//input[@name='quf_SequenceNum']"), "001"); - selectOptionByText(Locator.name("quf_ParticipantId"), "618005775"); - clickButton("Submit"); + detail.clickInsertNewRow() + .setField("GlobalUniqueId", "Global") + .setField("VisitDescription", "NewVisit") + .setField("SequenceNum", "001") + .setField("ParticipantId", OptionSelect.SelectOption.textOption("618005775")) + .submit(); assertElementNotPresent(Locator.tagWithClass("*", "labkey-error").withText()); detail.setFilter("VisitDescription", "Equals", "NewVisit"); assertTextPresent("NewVisit"); From a479eab76422256ce4ffe4915266955386078bae Mon Sep 17 00:00:00 2001 From: Josh Eckels Date: Mon, 29 Sep 2025 09:07:53 -0700 Subject: [PATCH 461/494] Remove deprecated methods from SQLFragment Original PR: https://github.com/LabKey/platform/pull/7080 * Remove deprecated methods from SQLFragment * Another deprecated method to remove --- .../specimen/importer/SpecimenImporter.java | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/specimen/src/org/labkey/specimen/importer/SpecimenImporter.java b/specimen/src/org/labkey/specimen/importer/SpecimenImporter.java index 2098172f..13e5ed7b 100644 --- a/specimen/src/org/labkey/specimen/importer/SpecimenImporter.java +++ b/specimen/src/org/labkey/specimen/importer/SpecimenImporter.java @@ -1215,7 +1215,7 @@ private SQLFragment getVialColsSql(List availableColumns) SQLFragment cols = new SQLFragment(); for (SpecimenColumn col : getVialCols(availableColumns)) { - cols.append(sep).append(col.getLegalDbColumnName(getSqlDialect())); + cols.append(sep).appendIdentifier(col.getLegalDbColumnName(getSqlDialect())); sep = ",\n "; } _vialColsSql = cols; @@ -1246,7 +1246,7 @@ private SQLFragment getSpecimenEventColsSql(List availableColumn SQLFragment cols = new SQLFragment(); for (SpecimenColumn col : getSpecimenEventCols(availableColumns)) { - cols.append(sep).append(col.getLegalDbColumnName(getSqlDialect())); + cols.append(sep).appendIdentifier(col.getLegalDbColumnName(getSqlDialect())); sep = ",\n "; } _vialEventColsSql = cols; @@ -1422,7 +1422,7 @@ private void appendConflictResolvingSQL(SqlDialect dialect, SQLFragment sql, Spe { if (null != castColumn) { - sql.append(" COUNT(DISTINCT ").append(tempTableName).append(".").append(castColumn.getLegalDbColumnName(getSqlDialect())).append(") = 1 THEN "); + sql.append(" COUNT(DISTINCT ").append(tempTableName).append(".").appendIdentifier(castColumn.getLegalDbColumnName(getSqlDialect())).append(") = 1 THEN "); sql.append("CAST(MIN(").append(selectCol).append(") AS ").append(castColumn.getDbType()).append(")"); } else @@ -1436,9 +1436,9 @@ private void appendConflictResolvingSQL(SqlDialect dialect, SQLFragment sql, Spe sql.append(" AS "); if (null != castColumn) - sql.append(castColumn.getLegalDbColumnName(getSqlDialect())); + sql.appendIdentifier(castColumn.getLegalDbColumnName(getSqlDialect())); else - sql.append(col.getLegalDbColumnName(getSqlDialect())); + sql.appendIdentifier(col.getLegalDbColumnName(getSqlDialect())); } @@ -1591,7 +1591,7 @@ private void populateVials(SpecimenLoadInfo info, boolean merge, boolean seenVis .add(Boolean.TRUE); for (SpecimenColumn col : getVialCols(info.getAvailableColumns())) - insertSelectSql.append(prefix).append("VialList.").append(col.getLegalDbColumnName(getSqlDialect())); + insertSelectSql.append(prefix).append("VialList.").appendIdentifier(col.getLegalDbColumnName(getSqlDialect())); insertSelectSql .append(" FROM (").append(getVialListFromTempTableSql(info, false, seenVisitValue)) @@ -2412,10 +2412,10 @@ protected void remapTempTableLookupIndexes(DbSchema schema, String tempTable, Li { if (col.getFkTable() != null) { - remapExternalIdsSql.append(sep).append(col.getLegalDbColumnName(getSqlDialect())).append(" = (SELECT RowId FROM ") + remapExternalIdsSql.append(sep).appendIdentifier(col.getLegalDbColumnName(getSqlDialect())).append(" = (SELECT RowId FROM ") .append(getTableInfoFromFkTableName(col.getFkTable()).getSelectName()).append(" ").append(col.getFkTableAlias()) .append(" WHERE ").append("(").append(tempTable).append(".") - .append(col.getLegalDbColumnName(getSqlDialect())).append(" = ").append(col.getFkTableAlias()).append(".").append(col.getFkColumn()) + .appendIdentifier(col.getLegalDbColumnName(getSqlDialect())).append(" = ").append(col.getFkTableAlias()).append(".").append(col.getFkColumn()) .append("))"); sep = ",\n\t"; } @@ -2610,7 +2610,7 @@ private void updateTempTableSpecimenHash(TempTablesHolder tempTablesHolder, List conflictResolvingSubselect.append(singletonAggregate); conflictResolvingSubselect.append(" ELSE NULL END"); } - conflictResolvingSubselect.append(" AS ").append(col.getLegalDbColumnName(getSqlDialect())); + conflictResolvingSubselect.append(" AS ").appendIdentifier(col.getLegalDbColumnName(getSqlDialect())); } } conflictResolvingSubselect.append("\nFROM ").append(tempTableName).append("\nGROUP BY GlobalUniqueId"); From e5628dda57916de11b8886547501924aad0eb3f0 Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Fri, 3 Oct 2025 15:06:11 -0700 Subject: [PATCH 462/494] Return Set> from Module "get test" methods Original PR: https://github.com/LabKey/platform/pull/7098 --- specimen/src/org/labkey/specimen/SpecimenModule.java | 5 ++--- .../labkey/specimen/view/SpecimenReportWebPartFactory.java | 2 +- .../labkey/specimen/view/SpecimenSearchWebPartFactory.java | 2 +- .../src/org/labkey/specimen/view/SpecimenWebPartFactory.java | 2 +- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/specimen/src/org/labkey/specimen/SpecimenModule.java b/specimen/src/org/labkey/specimen/SpecimenModule.java index afdde6c7..02ad4f3b 100644 --- a/specimen/src/org/labkey/specimen/SpecimenModule.java +++ b/specimen/src/org/labkey/specimen/SpecimenModule.java @@ -315,8 +315,7 @@ protected void startupAfterSpringConfig(ModuleContext moduleContext) } @Override - @NotNull - public Set getUnitTests() + public @NotNull Set> getUnitTests() { return Set.of( SpecimenWriter.TestCase.class @@ -330,7 +329,7 @@ public Set getUnitTests() } @Override - public @NotNull Set getIntegrationTests() + public @NotNull Set> getIntegrationTests() { return Set.of( SpecimenImporter.TestCase.class diff --git a/specimen/src/org/labkey/specimen/view/SpecimenReportWebPartFactory.java b/specimen/src/org/labkey/specimen/view/SpecimenReportWebPartFactory.java index 6eea9fc4..8c1ae4c6 100644 --- a/specimen/src/org/labkey/specimen/view/SpecimenReportWebPartFactory.java +++ b/specimen/src/org/labkey/specimen/view/SpecimenReportWebPartFactory.java @@ -67,7 +67,7 @@ private SpecimenVisitReportParameters getFactory(ViewContext context, Portal.Web } @Override - public HttpView getEditView(Portal.WebPart webPart, ViewContext context) + public HttpView getEditView(Portal.WebPart webPart, ViewContext context) { return new JspView<>("/org/labkey/specimen/view/customizeSpecimenReportWebPart.jsp", webPart); } diff --git a/specimen/src/org/labkey/specimen/view/SpecimenSearchWebPartFactory.java b/specimen/src/org/labkey/specimen/view/SpecimenSearchWebPartFactory.java index b820d411..977a814d 100644 --- a/specimen/src/org/labkey/specimen/view/SpecimenSearchWebPartFactory.java +++ b/specimen/src/org/labkey/specimen/view/SpecimenSearchWebPartFactory.java @@ -19,7 +19,7 @@ public SpecimenSearchWebPartFactory(String position) } @Override - public WebPartView getWebPartView(@NotNull ViewContext portalCtx, @NotNull Portal.WebPart webPart) + public WebPartView getWebPartView(@NotNull ViewContext portalCtx, @NotNull Portal.WebPart webPart) { if (!portalCtx.hasPermission(ReadPermission.class)) return new HtmlView("Specimens", HtmlString.of(portalCtx.getUser().isGuest() ? "Please log in to see this data." : "You do not have permission to see this data")); diff --git a/specimen/src/org/labkey/specimen/view/SpecimenWebPartFactory.java b/specimen/src/org/labkey/specimen/view/SpecimenWebPartFactory.java index e9c2dcfc..14b02ded 100644 --- a/specimen/src/org/labkey/specimen/view/SpecimenWebPartFactory.java +++ b/specimen/src/org/labkey/specimen/view/SpecimenWebPartFactory.java @@ -22,7 +22,7 @@ public SpecimenWebPartFactory() } @Override - public WebPartView getWebPartView(@NotNull ViewContext portalCtx, @NotNull Portal.WebPart webPart) + public WebPartView getWebPartView(@NotNull ViewContext portalCtx, @NotNull Portal.WebPart webPart) { if (!portalCtx.hasPermission(ReadPermission.class)) return new HtmlView("Specimens", HtmlString.of(portalCtx.getUser().isGuest() ? "Please log in to see this data." : "You do not have permission to see this data")); From 2a22521b27244583994f123add1a8a928872b984 Mon Sep 17 00:00:00 2001 From: Trey Chadick Date: Mon, 6 Oct 2025 10:08:46 -0700 Subject: [PATCH 463/494] Add explicit permissions to GitHub workflows Original PR: https://github.com/LabKey/testAutomation/pull/2723 --- src/org/labkey/test/tests/SpecimenTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/org/labkey/test/tests/SpecimenTest.java b/src/org/labkey/test/tests/SpecimenTest.java index 0277fc6e..6dea72c0 100644 --- a/src/org/labkey/test/tests/SpecimenTest.java +++ b/src/org/labkey/test/tests/SpecimenTest.java @@ -825,7 +825,7 @@ private void verifyRequestingLocationCounts(StudyLocationType... types) waitForElement(Locators.bodyTitle().withText("New Specimen Request")); int expectedLocationCount = StudyLocationType.untypedSites(); - long additionalLocations = Math.round(Math.pow(2, StudyLocationType.values().length - 1)); + int additionalLocations = (int) Math.round(Math.pow(2, StudyLocationType.values().length - 1)); for (StudyLocationType type : StudyLocationType.values()) { From b23a8584cdb4d1a7217fa9b5f3f3ca8571664c86 Mon Sep 17 00:00:00 2001 From: Nick Kerr Date: Wed, 29 Oct 2025 09:00:45 -0700 Subject: [PATCH 464/494] Replace AbstractContainerListener with ContainerListener default Original PR: https://github.com/LabKey/platform/pull/7154 --- .../specimen/SpecimenRequestContainerListener.java | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/specimen/src/org/labkey/specimen/SpecimenRequestContainerListener.java b/specimen/src/org/labkey/specimen/SpecimenRequestContainerListener.java index a9dfc275..96865519 100644 --- a/specimen/src/org/labkey/specimen/SpecimenRequestContainerListener.java +++ b/specimen/src/org/labkey/specimen/SpecimenRequestContainerListener.java @@ -1,13 +1,10 @@ package org.labkey.specimen; -import org.jetbrains.annotations.NotNull; import org.labkey.api.data.Container; import org.labkey.api.data.ContainerManager; import org.labkey.api.security.User; import java.beans.PropertyChangeEvent; -import java.util.Collection; -import java.util.Collections; public class SpecimenRequestContainerListener implements ContainerManager.ContainerListener { @@ -29,13 +26,6 @@ public void containerMoved(Container c, Container oldParent, User user) SpecimenRequestManager.get().clearCaches(c); } - @NotNull - @Override - public Collection canMove(Container c, Container newParent, User user) - { - return Collections.emptyList(); - } - @Override public void propertyChange(PropertyChangeEvent evt) { From 0e9ba9acc6d46c770d0291750a78628f6358c954 Mon Sep 17 00:00:00 2001 From: Josh Eckels Date: Tue, 4 Nov 2025 17:47:43 -0800 Subject: [PATCH 465/494] Adopt FileLike for more pipeline APIs Original PR: https://github.com/LabKey/platform/pull/7134 --- .../specimen/actions/SpecimenController.java | 15 ++++++++------- .../labkey/specimen/pipeline/SpecimenArchive.java | 12 ++++++------ .../labkey/specimen/pipeline/SpecimenBatch.java | 5 +++-- .../specimen/pipeline/SpecimenReloadJob.java | 5 +++-- .../pipeline/SpecimenReloadJobSupport.java | 3 ++- .../specimen/pipeline/SpecimenReloadTask.java | 3 +-- 6 files changed, 23 insertions(+), 20 deletions(-) diff --git a/specimen/src/org/labkey/specimen/actions/SpecimenController.java b/specimen/src/org/labkey/specimen/actions/SpecimenController.java index d3d18097..2d05110b 100644 --- a/specimen/src/org/labkey/specimen/actions/SpecimenController.java +++ b/specimen/src/org/labkey/specimen/actions/SpecimenController.java @@ -186,6 +186,7 @@ import org.labkey.specimen.view.SpecimenRequestNotificationEmailTemplate; import org.labkey.specimen.view.SpecimenSearchWebPart; import org.labkey.specimen.view.SpecimenWebPart; +import org.labkey.vfs.FileLike; import org.springframework.validation.BindException; import org.springframework.validation.Errors; import org.springframework.validation.ObjectError; @@ -919,7 +920,7 @@ public boolean isMerge() } } - public static void submitSpecimenBatch(Container c, User user, ActionURL url, File f, PipeRoot root, boolean merge) throws IOException + public static void submitSpecimenBatch(Container c, User user, ActionURL url, FileLike f, PipeRoot root, boolean merge) throws IOException { if (null == f || !f.exists() || !f.isFile()) throw new NotFoundException(); @@ -942,7 +943,7 @@ public boolean handlePost(PipelineForm form, BindException errors) throws Except Container c = getContainer(); PipeRoot root = PipelineService.get().findPipelineRoot(c); boolean first = true; - for (File f : form.getValidatedFiles(c)) + for (FileLike f : form.getValidatedFiles(c)) { // Only possibly overwrite when the first archive is loaded: boolean merge = !first || form.isMerge(); @@ -977,13 +978,13 @@ public boolean handlePost(PipelineForm form, BindException errors) throws Except { Container c = getContainer(); String path = form.getPath(); - File f = null; + FileLike f = null; PipeRoot root = PipelineService.get().findPipelineRoot(c); if (path != null) { if (root != null) - f = root.resolvePath(path); + f = root.resolvePathToFileLike(path); } submitSpecimenBatch(c, getUser(), getViewContext().getActionURL(), f, root, form.isMerge()); @@ -1083,18 +1084,18 @@ public class ImportSpecimenDataAction extends SimpleViewAction @Override public ModelAndView getView(PipelineForm form, BindException bindErrors) { - List dataFiles = form.getValidatedFiles(getContainer()); + List dataFiles = form.getValidatedFiles(getContainer()); List archives = new ArrayList<>(); List errors = new ArrayList<>(); _filePaths = form.getFile(); - for (File dataFile : dataFiles) + for (FileLike dataFile : dataFiles) { if (null == dataFile || !dataFile.exists() || !dataFile.isFile()) { throw new NotFoundException(); } - if (!dataFile.canRead()) + if (!dataFile.toNioPathForRead().toFile().canRead()) errors.add("Can't read data file: " + dataFile); SpecimenArchive archive = new SpecimenArchive(dataFile); diff --git a/specimen/src/org/labkey/specimen/pipeline/SpecimenArchive.java b/specimen/src/org/labkey/specimen/pipeline/SpecimenArchive.java index dc43d69d..781b8d86 100644 --- a/specimen/src/org/labkey/specimen/pipeline/SpecimenArchive.java +++ b/specimen/src/org/labkey/specimen/pipeline/SpecimenArchive.java @@ -19,8 +19,8 @@ import org.labkey.api.data.Container; import org.labkey.api.study.SpecimenService; import org.labkey.api.study.SpecimenTransform; +import org.labkey.vfs.FileLike; -import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Date; @@ -36,14 +36,14 @@ */ public class SpecimenArchive { - private final File _definitionFile; + private final FileLike _definitionFile; - public SpecimenArchive(File definitionFile) + public SpecimenArchive(FileLike definitionFile) { _definitionFile = definitionFile; } - public File getDefinitionFile() + public FileLike getDefinitionFile() { return _definitionFile; } @@ -56,13 +56,13 @@ public List getEntryDescriptions(Container container) throws I { if (transform.getFileType().isType(_definitionFile)) { - entryList.add(new EntryDescription(_definitionFile.getName(), _definitionFile.length(), new Date(_definitionFile.lastModified()))); + entryList.add(new EntryDescription(_definitionFile.getName(), _definitionFile.getSize(), new Date(_definitionFile.getLastModified()))); return entryList; } } // standard non-transformed specimen archive - try (ZipFile zip = new ZipFile(_definitionFile)) + try (ZipFile zip = new ZipFile(_definitionFile.toNioPathForRead().toFile())) { Enumeration entries = zip.entries(); while (entries.hasMoreElements()) diff --git a/specimen/src/org/labkey/specimen/pipeline/SpecimenBatch.java b/specimen/src/org/labkey/specimen/pipeline/SpecimenBatch.java index e072c092..b9c6a2ac 100644 --- a/specimen/src/org/labkey/specimen/pipeline/SpecimenBatch.java +++ b/specimen/src/org/labkey/specimen/pipeline/SpecimenBatch.java @@ -29,6 +29,7 @@ import org.labkey.api.util.PageFlowUtil; import org.labkey.api.view.ActionURL; import org.labkey.api.view.ViewBackgroundInfo; +import org.labkey.vfs.FileLike; import java.io.File; import java.io.Serializable; @@ -48,7 +49,7 @@ public class SpecimenBatch extends StudyBatch implements Serializable, SpecimenJ // For serialization protected SpecimenBatch() {} - public SpecimenBatch(ViewBackgroundInfo info, File definitionFile, PipeRoot root, boolean merge) + public SpecimenBatch(ViewBackgroundInfo info, FileLike definitionFile, PipeRoot root, boolean merge) { super(info, definitionFile, root); _isMerge = merge; @@ -78,7 +79,7 @@ public ActionURL getStatusHref() @Override public Path getSpecimenArchivePath() { - return _definitionFile.toPath(); + return _definitionFile.toNioPathForRead(); } @Override diff --git a/specimen/src/org/labkey/specimen/pipeline/SpecimenReloadJob.java b/specimen/src/org/labkey/specimen/pipeline/SpecimenReloadJob.java index c635f80c..4ddbbb6e 100644 --- a/specimen/src/org/labkey/specimen/pipeline/SpecimenReloadJob.java +++ b/specimen/src/org/labkey/specimen/pipeline/SpecimenReloadJob.java @@ -22,6 +22,7 @@ import org.labkey.api.study.SpecimenTransform; import org.labkey.api.util.FileUtil; import org.labkey.api.view.ViewBackgroundInfo; +import org.labkey.vfs.FileLike; import java.io.File; import java.io.Serializable; @@ -41,13 +42,13 @@ public SpecimenReloadJob(ViewBackgroundInfo info, PipeRoot root, String transfor { super(info, null, root, false); - File logFile = new File(root.getRootPath(), FileUtil.makeFileNameWithTimestamp("specimen_reload", "log")); + FileLike logFile = root.resolvePathToFileLike(FileUtil.makeFileNameWithTimestamp("specimen_reload", "log")); setLogFile(logFile); _transformName = transformName; } @Override - public void setSpecimenArchive(File archiveFile) + public void setSpecimenArchive(FileLike archiveFile) { _definitionFile = archiveFile; } diff --git a/specimen/src/org/labkey/specimen/pipeline/SpecimenReloadJobSupport.java b/specimen/src/org/labkey/specimen/pipeline/SpecimenReloadJobSupport.java index 42f6a61a..2ecc7cd0 100644 --- a/specimen/src/org/labkey/specimen/pipeline/SpecimenReloadJobSupport.java +++ b/specimen/src/org/labkey/specimen/pipeline/SpecimenReloadJobSupport.java @@ -16,6 +16,7 @@ package org.labkey.specimen.pipeline; import org.labkey.api.study.SpecimenTransform; +import org.labkey.vfs.FileLike; import java.io.File; @@ -24,7 +25,7 @@ */ public interface SpecimenReloadJobSupport extends SpecimenJobSupport { - void setSpecimenArchive(File archiveFile); + void setSpecimenArchive(FileLike archiveFile); String getSpecimenTransform(); diff --git a/specimen/src/org/labkey/specimen/pipeline/SpecimenReloadTask.java b/specimen/src/org/labkey/specimen/pipeline/SpecimenReloadTask.java index 9cd2bf6d..6a88f0e8 100644 --- a/specimen/src/org/labkey/specimen/pipeline/SpecimenReloadTask.java +++ b/specimen/src/org/labkey/specimen/pipeline/SpecimenReloadTask.java @@ -58,8 +58,7 @@ public RecordedActionSet run() throws PipelineJobException PipeRoot root = PipelineService.get().findPipelineRoot(job.getContainer()); if (root != null) { - FileLike archiveFileLike = root.getRootFileLike().resolveChild(FileUtil.makeFileNameWithTimestamp("specimen_reload", transform.getFileType().getDefaultSuffix())); - File archive = FileSystemLike.toFile(archiveFileLike); + FileLike archive = root.getRootFileLike().resolveChild(FileUtil.makeFileNameWithTimestamp("specimen_reload", transform.getFileType().getDefaultSuffix())); transform.importFromExternalSource(job, support.getExternalImportConfig(), archive); support.setSpecimenArchive(archive); From 70d1699236c43cee247e275b7cb842fb5bca0200 Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Tue, 11 Nov 2025 16:53:01 -0800 Subject: [PATCH 466/494] Rework attachment handling during database migrations Original PR: https://github.com/LabKey/platform/pull/7191 --- specimen/src/org/labkey/specimen/SpecimenServiceImpl.java | 8 ++++++++ .../labkey/specimen/model/SpecimenRequestEventType.java | 5 +++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/specimen/src/org/labkey/specimen/SpecimenServiceImpl.java b/specimen/src/org/labkey/specimen/SpecimenServiceImpl.java index 3ed5279f..5be823cc 100644 --- a/specimen/src/org/labkey/specimen/SpecimenServiceImpl.java +++ b/specimen/src/org/labkey/specimen/SpecimenServiceImpl.java @@ -19,6 +19,7 @@ import org.apache.logging.log4j.Logger; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.labkey.api.attachments.AttachmentType; import org.labkey.api.data.Container; import org.labkey.api.data.DbSchema; import org.labkey.api.data.PropertyManager; @@ -52,6 +53,7 @@ import org.labkey.api.view.ActionURL; import org.labkey.api.view.ViewBackgroundInfo; import org.labkey.specimen.importer.SpecimenColumn; +import org.labkey.specimen.model.SpecimenRequestEventType; import org.labkey.specimen.pipeline.SpecimenReloadJob; import org.labkey.specimen.requirements.SpecimenRequestRequirementProvider; @@ -366,6 +368,12 @@ public void registerRequestCustomizer(SpecimenRequestCustomizer customizer) _specimenRequestCustomizer = customizer; } + @Override + public AttachmentType getSpecimenRequestEventType() + { + return SpecimenRequestEventType.get(); + } + @Override public void fireSpecimensChanged(Container c, User user, Logger logger) { diff --git a/specimen/src/org/labkey/specimen/model/SpecimenRequestEventType.java b/specimen/src/org/labkey/specimen/model/SpecimenRequestEventType.java index a7bcd30c..b14f4d1d 100644 --- a/specimen/src/org/labkey/specimen/model/SpecimenRequestEventType.java +++ b/specimen/src/org/labkey/specimen/model/SpecimenRequestEventType.java @@ -16,6 +16,7 @@ package org.labkey.specimen.model; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.labkey.api.attachments.AttachmentType; import org.labkey.api.data.SQLFragment; import org.labkey.api.specimen.SpecimenSchema; @@ -40,8 +41,8 @@ private SpecimenRequestEventType() } @Override - public void addWhereSql(SQLFragment sql, String parentColumn, String documentNameColumn) + public @Nullable SQLFragment getSelectParentEntityIdsSql() { - sql.append(parentColumn).append(" IN (SELECT EntityId FROM ").append(SpecimenSchema.get().getTableInfoSampleRequestEvent(), "sre").append(")"); + return new SQLFragment("SELECT EntityId FROM ").append(SpecimenSchema.get().getTableInfoSampleRequestEvent(), "sre"); } } \ No newline at end of file From f6ecabcefa46005a72af2fb56614052a48bc7678 Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Mon, 1 Dec 2025 11:21:29 -0800 Subject: [PATCH 467/494] Remove all support for legacy audit union table Original PR: https://github.com/LabKey/platform/pull/7230 --- .../labkey/specimen/SpecimenCommentAuditProvider.java | 9 --------- 1 file changed, 9 deletions(-) diff --git a/specimen/src/org/labkey/specimen/SpecimenCommentAuditProvider.java b/specimen/src/org/labkey/specimen/SpecimenCommentAuditProvider.java index d7ddb3a9..f15aa3df 100644 --- a/specimen/src/org/labkey/specimen/SpecimenCommentAuditProvider.java +++ b/specimen/src/org/labkey/specimen/SpecimenCommentAuditProvider.java @@ -37,7 +37,6 @@ import org.labkey.specimen.actions.SpecimenController; import java.util.List; -import java.util.Map; import java.util.Set; import static org.labkey.specimen.SpecimenCommentAuditDomainKind.COLUMN_NAME_VIAL_ID; @@ -78,14 +77,6 @@ public String getDescription() return "Specimen Comments and QC"; } - @Override - public Map legacyNameMap() - { - Map legacyNames = super.legacyNameMap(); - legacyNames.put(FieldKey.fromParts("key1"), COLUMN_NAME_VIAL_ID); - return legacyNames; - } - @Override public Class getEventClass() { From f3f36ccbc1beca08389e6b93b043470df9fd6aee Mon Sep 17 00:00:00 2001 From: Josh Eckels Date: Wed, 3 Dec 2025 14:31:32 -0800 Subject: [PATCH 468/494] Continue adopting FileLike instead of File or Path Original PR: https://github.com/LabKey/platform/pull/7211 --- .../org/labkey/specimen/SpecimenModule.java | 4 +- .../importer/AbstractSpecimenTask.java | 61 ++++++++----------- .../pipeline/FileAnalysisSpecimenTask.java | 21 +++---- .../specimen/pipeline/SpecimenBatch.java | 10 ++- .../specimen/pipeline/SpecimenJobSupport.java | 5 +- 5 files changed, 45 insertions(+), 56 deletions(-) diff --git a/specimen/src/org/labkey/specimen/SpecimenModule.java b/specimen/src/org/labkey/specimen/SpecimenModule.java index 02ad4f3b..745a9a16 100644 --- a/specimen/src/org/labkey/specimen/SpecimenModule.java +++ b/specimen/src/org/labkey/specimen/SpecimenModule.java @@ -90,8 +90,8 @@ import org.labkey.specimen.writer.SpecimenArchiveWriter; import org.labkey.specimen.writer.SpecimenSettingsWriter; import org.labkey.specimen.writer.SpecimenWriter; +import org.labkey.vfs.FileLike; -import java.nio.file.Path; import java.util.Collection; import java.util.List; import java.util.Map; @@ -171,7 +171,7 @@ public ActionURL getSpecimensURL(Container c) } @Override - public void importSpecimenArchive(@Nullable Path inputFile, PipelineJob job, SimpleStudyImportContext ctx, boolean merge, boolean syncParticipantVisit) throws PipelineJobException, ValidationException + public void importSpecimenArchive(@Nullable FileLike inputFile, PipelineJob job, SimpleStudyImportContext ctx, boolean merge, boolean syncParticipantVisit) throws PipelineJobException, ValidationException { AbstractSpecimenTask.doImport(inputFile, job, ctx, merge, syncParticipantVisit); } diff --git a/specimen/src/org/labkey/specimen/importer/AbstractSpecimenTask.java b/specimen/src/org/labkey/specimen/importer/AbstractSpecimenTask.java index ff74eb52..3acf4e95 100644 --- a/specimen/src/org/labkey/specimen/importer/AbstractSpecimenTask.java +++ b/specimen/src/org/labkey/specimen/importer/AbstractSpecimenTask.java @@ -16,7 +16,6 @@ package org.labkey.specimen.importer; -import org.apache.logging.log4j.Logger; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.labkey.api.admin.ImportException; @@ -25,7 +24,6 @@ import org.labkey.api.pipeline.PipelineJobException; import org.labkey.api.pipeline.RecordedActionSet; import org.labkey.api.pipeline.TaskFactory; -import org.labkey.api.query.ValidationException; import org.labkey.api.study.SpecimenService; import org.labkey.api.study.SpecimenTransform; import org.labkey.api.study.importer.SimpleStudyImportContext; @@ -37,11 +35,9 @@ import org.labkey.api.writer.ZipUtil; import org.labkey.specimen.pipeline.SpecimenJobSupport; import org.labkey.specimen.writer.SpecimenArchiveDataTypes; +import org.labkey.vfs.FileLike; -import java.io.File; import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; import java.sql.BatchUpdateException; import java.util.Date; @@ -66,7 +62,7 @@ public RecordedActionSet run() throws PipelineJobException try { PipelineJob job = getJob(); - Path specimenArchive = getSpecimenFile(job); + FileLike specimenArchive = getSpecimenFile(job); SimpleStudyImportContext ctx = getImportContext(job); doImport(specimenArchive, job, ctx, isMerge(), true, getImportHelper()); @@ -83,7 +79,7 @@ public RecordedActionSet run() throws PipelineJobException return new RecordedActionSet(); } - protected Path getSpecimenFile(PipelineJob job) throws Exception + protected FileLike getSpecimenFile(PipelineJob job) throws Exception { SpecimenJobSupport support = job.getJobSupport(SpecimenJobSupport.class); return support.getSpecimenArchivePath(); @@ -94,25 +90,30 @@ protected SimpleStudyImportContext getImportContext(PipelineJob job) return job.getJobSupport(SpecimenJobSupport.class).getImportContext(); } - public static void doImport(@Nullable Path inputFile, PipelineJob job, SimpleStudyImportContext ctx, boolean merge, - boolean syncParticipantVisit) throws PipelineJobException, ValidationException + public static void doImport(@Nullable FileLike inputFile, PipelineJob job, SimpleStudyImportContext ctx, boolean merge, + boolean syncParticipantVisit) throws PipelineJobException { doImport(inputFile, job, ctx, merge, syncParticipantVisit, new DefaultImportHelper()); } - private static void doPostTransform(SpecimenTransform transformer, Path inputFile, PipelineJob job) throws PipelineJobException + private static void doPostTransform(SpecimenTransform transformer, FileLike inputFile, PipelineJob job) throws PipelineJobException { if (transformer.getFileType().isType(inputFile)) { if (job != null) job.setStatus("OPTIONAL POST TRANSFORMING STEP " + transformer.getName() + " DATA"); - File specimenArchive = ARCHIVE_FILE_TYPE.getFile(inputFile.getParent().toFile(), transformer.getFileType().getBaseName(inputFile)); - transformer.postTransform(job, inputFile.toFile(), specimenArchive); + FileLike specimenArchive = ARCHIVE_FILE_TYPE.getFile(inputFile.getParent(), transformer.getFileType().getBaseName(inputFile)); + transformer.postTransform(job, inputFile, specimenArchive); } } - public static void doImport(@Nullable Path inputFile, PipelineJob job, SimpleStudyImportContext ctx, boolean merge, - boolean syncParticipantVisit, ImportHelper importHelper) throws PipelineJobException, ValidationException + public AbstractSpecimenTask(TaskFactory factory, PipelineJob job) + { + super(factory, job); + } + + public static void doImport(@Nullable FileLike inputFile, PipelineJob job, SimpleStudyImportContext ctx, boolean merge, + boolean syncParticipantVisit, ImportHelper importHelper) throws PipelineJobException { // do nothing if we've specified data types and specimen is not one of them if (!ctx.isDataTypeSelected(SpecimenArchiveDataTypes.SPECIMENS)) @@ -157,8 +158,8 @@ public static void doImport(@Nullable Path inputFile, PipelineJob job, SimpleStu } catch (Exception e) { - if (e instanceof BatchUpdateException && null != ((BatchUpdateException)e).getNextException()) - e = ((BatchUpdateException)e).getNextException(); + if (e instanceof BatchUpdateException bue && null != bue.getNextException()) + e = bue.getNextException(); throw new PipelineJobException(e); } finally @@ -180,30 +181,30 @@ public ImportHelper getImportHelper() public interface ImportHelper { - VirtualFile getSpecimenDir(PipelineJob job, SimpleStudyImportContext ctx, @Nullable Path inputFile) throws IOException, ImportException, PipelineJobException; + VirtualFile getSpecimenDir(PipelineJob job, SimpleStudyImportContext ctx, @Nullable FileLike inputFile) throws IOException, ImportException, PipelineJobException; void afterImport(SimpleStudyImportContext ctx); } protected static class DefaultImportHelper implements ImportHelper { - private Path _unzipDir; + private FileLike _unzipDir; @Override - public VirtualFile getSpecimenDir(PipelineJob job, SimpleStudyImportContext ctx, @Nullable Path inputFile) throws IOException, ImportException, PipelineJobException + public VirtualFile getSpecimenDir(PipelineJob job, SimpleStudyImportContext ctx, @Nullable FileLike inputFile) throws IOException, ImportException, PipelineJobException { // backwards compatibility, if we are given a specimen archive as a zip file, we need to extract it if (inputFile != null) { // Might need to transform to a file type that we know how to import - Path specimenArchive = inputFile; + FileLike specimenArchive = inputFile; for (SpecimenTransform transformer : SpecimenService.get().getSpecimenTransforms(ctx.getContainer())) { - if (transformer.getFileType().isType(inputFile.getFileName().toString())) + if (transformer.getFileType().isType(inputFile.getName())) { if (job != null) job.setStatus("TRANSFORMING " + transformer.getName() + " DATA"); - specimenArchive = ARCHIVE_FILE_TYPE.getPath(inputFile.getParent(), transformer.getFileType().getBaseName(inputFile)); + specimenArchive = ARCHIVE_FILE_TYPE.getFile(inputFile.getParent(), transformer.getFileType().getBaseName(inputFile)); transformer.transform(job, inputFile, specimenArchive); break; } @@ -221,7 +222,7 @@ public VirtualFile getSpecimenDir(PipelineJob job, SimpleStudyImportContext ctx, ctx.getLogger().info("Unzipping specimen archive " + specimenArchive); String tempDirName = DateUtil.formatDateTime(new Date(), "yyMMddHHmmssSSS"); - _unzipDir = specimenArchive.getParent().resolve(tempDirName); + _unzipDir = specimenArchive.getParent().resolveChild(tempDirName); ZipUtil.unzipToDirectory(specimenArchive, _unzipDir, ctx.getLogger()); ctx.getLogger().info("Archive unzipped to " + _unzipDir); @@ -242,19 +243,11 @@ public void afterImport(SimpleStudyImportContext ctx) delete(_unzipDir, ctx); } - protected void delete(Path path, SimpleStudyImportContext ctx) + protected void delete(FileLike path, SimpleStudyImportContext ctx) { - Logger log = ctx.getLogger(); - if (Files.isDirectory(path)) + if (path.isDirectory()) { - try - { - FileUtil.deleteDir(path); - } - catch (IOException e) - { - log.error("Error deleting files from " + path, e); - } + FileUtil.deleteDir(path); } } } diff --git a/specimen/src/org/labkey/specimen/pipeline/FileAnalysisSpecimenTask.java b/specimen/src/org/labkey/specimen/pipeline/FileAnalysisSpecimenTask.java index 05b37f3d..546b5e0d 100644 --- a/specimen/src/org/labkey/specimen/pipeline/FileAnalysisSpecimenTask.java +++ b/specimen/src/org/labkey/specimen/pipeline/FileAnalysisSpecimenTask.java @@ -18,10 +18,9 @@ import org.labkey.api.util.DateUtil; import org.labkey.api.writer.FileSystemFile; import org.labkey.api.writer.VirtualFile; +import org.labkey.vfs.FileLike; import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; import java.util.Date; import java.util.List; @@ -36,10 +35,10 @@ public FileAnalysisSpecimenTask(FileAnalysisSpecimenTask.Factory factory, Pipeli } @Override - protected Path getSpecimenFile(PipelineJob job) + protected FileLike getSpecimenFile(PipelineJob job) { FileAnalysisJobSupport support = job.getJobSupport(FileAnalysisJobSupport.class); - List paths = support.getInputFilePaths(); + List paths = support.getInputFiles(); // there should only be a single file associated with this task assert paths.size() == 1; return paths.get(0); @@ -60,7 +59,7 @@ protected boolean isMerge() if (support.getParameters().containsKey(MERGE_SPECIMEN)) isMerge = BooleanUtils.toBoolean(support.getParameters().get(MERGE_SPECIMEN)); - getJob().getLogger().info("Specimen merge option is set to : " + isMerge); + getJob().getLogger().info("Specimen merge option is set to: {}", isMerge); return isMerge; } @@ -72,14 +71,14 @@ public AbstractSpecimenTask.ImportHelper getImportHelper() private static class ImportHelper extends DefaultImportHelper { - private Path _tempDir; + private FileLike _tempDir; @Override - public VirtualFile getSpecimenDir(PipelineJob job, SimpleStudyImportContext ctx, @Nullable Path inputFile) throws IOException, ImportException, PipelineJobException + public VirtualFile getSpecimenDir(PipelineJob job, SimpleStudyImportContext ctx, @Nullable FileLike inputFile) throws ImportException, PipelineJobException { if (inputFile != null) { - try (TikaInputStream is = TikaInputStream.get(Files.newInputStream(inputFile))) + try (TikaInputStream is = TikaInputStream.get(inputFile.openInputStream())) { // determine the type of file being imported DefaultDetector detector = new DefaultDetector(); @@ -97,9 +96,9 @@ else if (MediaType.TEXT_PLAIN.equals(type)) // ctx.getLogger().info("Single specimen file detected, moving to a temp folder for processing."); String tempDirName = DateUtil.formatDateTime(new Date(), "yyMMddHHmmssSSS"); - _tempDir = inputFile.getParent().resolve(tempDirName); + _tempDir = inputFile.getParent().resolveChild(tempDirName); FileUtil.createDirectory(_tempDir); - Files.copy(inputFile, _tempDir.resolve(inputFile.getFileName())); + FileUtil.copyFile(inputFile, _tempDir.resolveChild(inputFile.getName())); return new FileSystemFile(_tempDir); } @@ -132,7 +131,7 @@ public Factory() } @Override - public PipelineJob.Task createTask(PipelineJob job) + public FileAnalysisSpecimenTask createTask(PipelineJob job) { return new FileAnalysisSpecimenTask(this, job); } diff --git a/specimen/src/org/labkey/specimen/pipeline/SpecimenBatch.java b/specimen/src/org/labkey/specimen/pipeline/SpecimenBatch.java index b9c6a2ac..2cd5d288 100644 --- a/specimen/src/org/labkey/specimen/pipeline/SpecimenBatch.java +++ b/specimen/src/org/labkey/specimen/pipeline/SpecimenBatch.java @@ -31,9 +31,7 @@ import org.labkey.api.view.ViewBackgroundInfo; import org.labkey.vfs.FileLike; -import java.io.File; import java.io.Serializable; -import java.nio.file.Path; /** * User: brittp @@ -56,9 +54,9 @@ public SpecimenBatch(ViewBackgroundInfo info, FileLike definitionFile, PipeRoot } @Override - protected File createLogFile() + protected FileLike createLogFile() { - return new File(getPipeRoot().getLogDirectory(), FileUtil.makeFileNameWithTimestamp(_definitionFile.getName(), "log")); + return getPipeRoot().getLogDirectory(true).resolveChild(FileUtil.makeFileNameWithTimestamp(_definitionFile.getName(), "log")); } @Override @@ -77,9 +75,9 @@ public ActionURL getStatusHref() } @Override - public Path getSpecimenArchivePath() + public FileLike getSpecimenArchivePath() { - return _definitionFile.toNioPathForRead(); + return _definitionFile; } @Override diff --git a/specimen/src/org/labkey/specimen/pipeline/SpecimenJobSupport.java b/specimen/src/org/labkey/specimen/pipeline/SpecimenJobSupport.java index fb9b983d..11043224 100644 --- a/specimen/src/org/labkey/specimen/pipeline/SpecimenJobSupport.java +++ b/specimen/src/org/labkey/specimen/pipeline/SpecimenJobSupport.java @@ -18,8 +18,7 @@ import org.labkey.api.admin.ImportException; import org.labkey.api.study.importer.SimpleStudyImportContext; - -import java.nio.file.Path; +import org.labkey.vfs.FileLike; /* * User: adam @@ -28,7 +27,7 @@ */ public interface SpecimenJobSupport { - Path getSpecimenArchivePath() throws ImportException; + FileLike getSpecimenArchivePath() throws ImportException; boolean isMerge(); SimpleStudyImportContext getImportContext(); } From 24ee19eaf7be671a997ef20a4d2178c9426b8977 Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Thu, 4 Dec 2025 09:33:42 -0800 Subject: [PATCH 469/494] Add and populate core.Documents.ParentType column Original PR: https://github.com/LabKey/platform/pull/7231 --- specimen/src/org/labkey/specimen/SpecimenModule.java | 2 +- specimen/src/org/labkey/specimen/SpecimenServiceImpl.java | 4 ++-- .../src/org/labkey/specimen/model/SpecimenRequestEvent.java | 4 ++-- .../org/labkey/specimen/model/SpecimenRequestEventType.java | 6 +++--- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/specimen/src/org/labkey/specimen/SpecimenModule.java b/specimen/src/org/labkey/specimen/SpecimenModule.java index 745a9a16..f4baed4e 100644 --- a/specimen/src/org/labkey/specimen/SpecimenModule.java +++ b/specimen/src/org/labkey/specimen/SpecimenModule.java @@ -140,7 +140,7 @@ protected void init() RoleManager.registerRole(new SpecimenCoordinatorRole()); RoleManager.registerRole(new SpecimenRequesterRole()); - AttachmentService.get().registerAttachmentType(SpecimenRequestEventType.get()); + AttachmentService.get().registerAttachmentParentType(SpecimenRequestEventType.get()); addController("specimen", SpecimenController.class, "study-samples"); addController("specimen-api", SpecimenApiController.class, "study-samples-api", "specimens-api"); diff --git a/specimen/src/org/labkey/specimen/SpecimenServiceImpl.java b/specimen/src/org/labkey/specimen/SpecimenServiceImpl.java index 5be823cc..6979b368 100644 --- a/specimen/src/org/labkey/specimen/SpecimenServiceImpl.java +++ b/specimen/src/org/labkey/specimen/SpecimenServiceImpl.java @@ -19,7 +19,7 @@ import org.apache.logging.log4j.Logger; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.labkey.api.attachments.AttachmentType; +import org.labkey.api.attachments.AttachmentParentType; import org.labkey.api.data.Container; import org.labkey.api.data.DbSchema; import org.labkey.api.data.PropertyManager; @@ -369,7 +369,7 @@ public void registerRequestCustomizer(SpecimenRequestCustomizer customizer) } @Override - public AttachmentType getSpecimenRequestEventType() + public AttachmentParentType getSpecimenRequestEventType() { return SpecimenRequestEventType.get(); } diff --git a/specimen/src/org/labkey/specimen/model/SpecimenRequestEvent.java b/specimen/src/org/labkey/specimen/model/SpecimenRequestEvent.java index 3b0bfea3..7feedcce 100644 --- a/specimen/src/org/labkey/specimen/model/SpecimenRequestEvent.java +++ b/specimen/src/org/labkey/specimen/model/SpecimenRequestEvent.java @@ -18,7 +18,7 @@ import org.jetbrains.annotations.NotNull; import org.labkey.api.attachments.AttachmentParent; -import org.labkey.api.attachments.AttachmentType; +import org.labkey.api.attachments.AttachmentParentType; import org.labkey.api.data.Container; import org.labkey.api.study.AbstractStudyCachable; @@ -152,7 +152,7 @@ public void setRequirementId(Integer requirementId) } @Override - public @NotNull AttachmentType getAttachmentType() + public @NotNull AttachmentParentType getAttachmentParentType() { return SpecimenRequestEventType.get(); } diff --git a/specimen/src/org/labkey/specimen/model/SpecimenRequestEventType.java b/specimen/src/org/labkey/specimen/model/SpecimenRequestEventType.java index b14f4d1d..b30e6e1f 100644 --- a/specimen/src/org/labkey/specimen/model/SpecimenRequestEventType.java +++ b/specimen/src/org/labkey/specimen/model/SpecimenRequestEventType.java @@ -17,11 +17,11 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.labkey.api.attachments.AttachmentType; +import org.labkey.api.attachments.AttachmentParentType; import org.labkey.api.data.SQLFragment; import org.labkey.api.specimen.SpecimenSchema; -public class SpecimenRequestEventType implements AttachmentType +public class SpecimenRequestEventType implements AttachmentParentType { private static final SpecimenRequestEventType INSTANCE = new SpecimenRequestEventType(); @@ -37,7 +37,7 @@ private SpecimenRequestEventType() @Override public @NotNull String getUniqueName() { - return getClass().getName(); + return "SpecimenRequest"; } @Override From 36411286e11b5491d5c3ede9169fcea50059df76 Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Wed, 10 Dec 2025 09:58:01 -0800 Subject: [PATCH 470/494] Write file paths, etc. Original PR: https://github.com/LabKey/platform/pull/7229 --- .../src/org/labkey/specimen/importer/SpecimenImporter.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/specimen/src/org/labkey/specimen/importer/SpecimenImporter.java b/specimen/src/org/labkey/specimen/importer/SpecimenImporter.java index 13e5ed7b..86e5d046 100644 --- a/specimen/src/org/labkey/specimen/importer/SpecimenImporter.java +++ b/specimen/src/org/labkey/specimen/importer/SpecimenImporter.java @@ -57,6 +57,7 @@ import org.labkey.api.dataiterator.DataIteratorBuilder; import org.labkey.api.dataiterator.DataIteratorContext; import org.labkey.api.dataiterator.DataIteratorUtil; +import org.labkey.api.dataiterator.ImportProgress; import org.labkey.api.dataiterator.ListofMapsDataIterator; import org.labkey.api.dataiterator.LoggingDataIterator; import org.labkey.api.dataiterator.MapDataIterator; @@ -69,7 +70,6 @@ import org.labkey.api.exp.api.ExpSampleType; import org.labkey.api.exp.api.ExperimentService; import org.labkey.api.exp.api.SampleTypeService; -import org.labkey.api.exp.list.ListImportProgress; import org.labkey.api.iterator.MarkableIterator; import org.labkey.api.pipeline.PipelineJob; import org.labkey.api.query.DefaultSchema; @@ -1993,7 +1993,7 @@ public Object getValue(Map row) DataIteratorBuilder standardEtl = StandardDataIteratorBuilder.forInsert(target, specimenWrapped, getContainer(), getUser(), dix); DataIteratorBuilder persist = ((UpdateableTableInfo)target).persistRows(standardEtl, dix); Pump pump = new Pump(persist, dix); - pump.setProgress(new ListImportProgress() + pump.setProgress(new ImportProgress() { long heartBeat = HeartBeat.currentTimeMillis(); From 01114240eb4f4180c4db98609e67fd95bbdb2076 Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Fri, 16 Jan 2026 12:09:38 -0800 Subject: [PATCH 471/494] Join core.Documents rows to parent details Original PR: https://github.com/LabKey/platform/pull/7319 --- .../specimen/model/SpecimenRequestEventType.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/specimen/src/org/labkey/specimen/model/SpecimenRequestEventType.java b/specimen/src/org/labkey/specimen/model/SpecimenRequestEventType.java index b30e6e1f..ad10c6a8 100644 --- a/specimen/src/org/labkey/specimen/model/SpecimenRequestEventType.java +++ b/specimen/src/org/labkey/specimen/model/SpecimenRequestEventType.java @@ -16,9 +16,9 @@ package org.labkey.specimen.model; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import org.labkey.api.attachments.AttachmentParentType; import org.labkey.api.data.SQLFragment; +import org.labkey.api.data.TableInfo; import org.labkey.api.specimen.SpecimenSchema; public class SpecimenRequestEventType implements AttachmentParentType @@ -41,8 +41,12 @@ private SpecimenRequestEventType() } @Override - public @Nullable SQLFragment getSelectParentEntityIdsSql() + public @NotNull SQLFragment getSelectEntityIdAndDescriptionSql() { - return new SQLFragment("SELECT EntityId FROM ").append(SpecimenSchema.get().getTableInfoSampleRequestEvent(), "sre"); + TableInfo table = SpecimenSchema.get().getTableInfoSampleRequestEvent(); + return new SQLFragment("SELECT EntityId, ") + .append(table.getSqlDialect().concatenate("'RowId:'", "CAST(RowId AS VARCHAR)")) + .append(" AS Description FROM ") + .append(table, "sre"); } } \ No newline at end of file From 2721a1de5527b109ee8eeb53261c379bc62c5fce Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Mon, 23 Mar 2026 10:47:44 -0700 Subject: [PATCH 472/494] Stop passing SecurityPolicy parameter to Role.isApplicable() Original PR: https://github.com/LabKey/platform/pull/7505 --- .../labkey/specimen/security/roles/AbstractSpecimenRole.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/specimen/src/org/labkey/specimen/security/roles/AbstractSpecimenRole.java b/specimen/src/org/labkey/specimen/security/roles/AbstractSpecimenRole.java index 7a7efd70..97832c3c 100644 --- a/specimen/src/org/labkey/specimen/security/roles/AbstractSpecimenRole.java +++ b/specimen/src/org/labkey/specimen/security/roles/AbstractSpecimenRole.java @@ -17,7 +17,6 @@ import org.labkey.api.data.Container; import org.labkey.api.security.SecurableResource; -import org.labkey.api.security.SecurityPolicy; import org.labkey.api.security.permissions.Permission; import org.labkey.api.security.roles.AbstractRole; import org.labkey.api.study.StudyService; @@ -37,9 +36,9 @@ protected AbstractSpecimenRole(String name, String description, Class Date: Mon, 23 Mar 2026 12:50:30 -0700 Subject: [PATCH 473/494] Move query-based specimen import to Specimen module Original PR: https://github.com/LabKey/platform/pull/7497 --- .../org/labkey/specimen/SpecimenModule.java | 11 +- .../specimen/actions/SpecimenController.java | 143 +++++++++++++++- .../importer/AbstractSpecimenTask.java | 29 ++-- .../specimen/importer/QueryBasedExport.java | 97 +++++++++++ .../QueryBasedSpecimenImportUploadTask.java | 81 +++++++++ .../QueryBasedSpecimenReloadConfig.java | 58 +++++++ .../importer/QueryBasedSpecimenTransform.java | 128 ++++++++++++++ .../importer/QueryBasedTransformTask.java | 160 ++++++++++++++++++ .../labkey/specimen/view/chooseImporter.jsp | 30 +--- .../specimen/view/configureQueryImport.jsp | 132 +++++++++++++++ .../test/sampledata/import/100_specimens.xlsx | Bin 0 -> 20576 bytes .../specimen/ConfigureSpecimenImportPage.java | 96 +++++++++++ .../QueryBasedSpecimenImportTest.java | 114 +++++++++++++ 13 files changed, 1041 insertions(+), 38 deletions(-) create mode 100644 specimen/src/org/labkey/specimen/importer/QueryBasedExport.java create mode 100644 specimen/src/org/labkey/specimen/importer/QueryBasedSpecimenImportUploadTask.java create mode 100644 specimen/src/org/labkey/specimen/importer/QueryBasedSpecimenReloadConfig.java create mode 100644 specimen/src/org/labkey/specimen/importer/QueryBasedSpecimenTransform.java create mode 100644 specimen/src/org/labkey/specimen/importer/QueryBasedTransformTask.java create mode 100644 specimen/src/org/labkey/specimen/view/configureQueryImport.jsp create mode 100644 specimen/test/sampledata/import/100_specimens.xlsx create mode 100644 specimen/test/src/org/labkey/test/pages/specimen/ConfigureSpecimenImportPage.java create mode 100644 specimen/test/src/org/labkey/test/tests/specimen/QueryBasedSpecimenImportTest.java diff --git a/specimen/src/org/labkey/specimen/SpecimenModule.java b/specimen/src/org/labkey/specimen/SpecimenModule.java index f4baed4e..833af41d 100644 --- a/specimen/src/org/labkey/specimen/SpecimenModule.java +++ b/specimen/src/org/labkey/specimen/SpecimenModule.java @@ -57,6 +57,7 @@ import org.labkey.api.study.importer.SimpleStudyImporterRegistry; import org.labkey.api.study.writer.SimpleStudyWriterRegistry; import org.labkey.api.usageMetrics.UsageMetricsService; +import org.labkey.api.util.SystemMaintenance; import org.labkey.api.util.emailTemplate.EmailTemplateService; import org.labkey.api.util.logging.LogHelper; import org.labkey.api.view.ActionURL; @@ -66,6 +67,8 @@ import org.labkey.specimen.actions.SpecimenApiController; import org.labkey.specimen.actions.SpecimenController; import org.labkey.specimen.importer.AbstractSpecimenTask; +import org.labkey.specimen.importer.QueryBasedSpecimenImportUploadTask; +import org.labkey.specimen.importer.QueryBasedSpecimenTransform; import org.labkey.specimen.importer.RequestabilityManager; import org.labkey.specimen.importer.SpecimenImporter; import org.labkey.specimen.importer.SpecimenSchemaImporter; @@ -232,7 +235,9 @@ public void addSpecimenPivotTableNames(Set names) return null; } }); - } + + SystemMaintenance.addTask(new QueryBasedSpecimenImportUploadTask()); + } @Override protected void startupAfterSpringConfig(ModuleContext moduleContext) @@ -254,6 +259,10 @@ protected void startupAfterSpringConfig(ModuleContext moduleContext) new SpecimenSettingsImporter() )); + SpecimenService specimenService = SpecimenService.get(); + if (null != specimenService) + specimenService.registerSpecimenTransform(new QueryBasedSpecimenTransform()); + UsageMetricsService svc = UsageMetricsService.get(); if (null != svc) { diff --git a/specimen/src/org/labkey/specimen/actions/SpecimenController.java b/specimen/src/org/labkey/specimen/actions/SpecimenController.java index 2d05110b..e2fd64a0 100644 --- a/specimen/src/org/labkey/specimen/actions/SpecimenController.java +++ b/specimen/src/org/labkey/specimen/actions/SpecimenController.java @@ -64,8 +64,10 @@ import org.labkey.api.module.FolderType; import org.labkey.api.module.Module; import org.labkey.api.pipeline.PipeRoot; +import org.labkey.api.pipeline.PipelineJob; import org.labkey.api.pipeline.PipelineService; import org.labkey.api.pipeline.PipelineStatusUrls; +import org.labkey.api.pipeline.PipelineValidationException; import org.labkey.api.pipeline.browse.PipelinePathForm; import org.labkey.api.query.AbstractQueryImportAction; import org.labkey.api.query.BatchValidationException; @@ -152,6 +154,7 @@ import org.labkey.specimen.SpecimenRequestException; import org.labkey.specimen.SpecimenRequestManager; import org.labkey.specimen.SpecimenRequestStatus; +import org.labkey.specimen.importer.QueryBasedSpecimenTransform; import org.labkey.specimen.importer.RequestabilityManager; import org.labkey.specimen.importer.SimpleSpecimenImporter; import org.labkey.specimen.model.ExtendedSpecimenRequestView; @@ -195,7 +198,6 @@ import java.io.BufferedOutputStream; import java.io.ByteArrayOutputStream; -import java.io.File; import java.io.IOException; import java.io.OutputStream; import java.nio.charset.StandardCharsets; @@ -5759,4 +5761,143 @@ public void addNavTrail(NavTree root) root.addChild("Insert " + _form.getQueryName()); } } + + public static class ConfigForm + { + private String _schemaName; + private String _queryName; + private String _viewName; + private String _enabled; + private int _userId; + + public int getUserId() + { + return _userId; + } + + public void setUserId(int userId) + { + _userId = userId; + } + + public String getSchemaName() + { + return _schemaName; + } + + public void setSchemaName(String schemaName) + { + _schemaName = schemaName; + } + + public String getQueryName() + { + return _queryName; + } + + public void setQueryName(String queryName) + { + _queryName = queryName; + } + + public String getViewName() + { + return _viewName; + } + + public void setViewName(String viewName) + { + _viewName = viewName; + } + + public String getEnabled() + { + return _enabled; + } + + public void setEnabled(String enabled) + { + _enabled = enabled; + } + + public Map getOptions() + { + Map valueMap = new HashMap<>(); + valueMap.put("schemaName", _schemaName); + valueMap.put("queryName", _queryName); + valueMap.put("viewName", _viewName); + valueMap.put("enabled", _enabled); + valueMap.put("userId", String.valueOf(_userId)); + return valueMap; + } + } + + @RequiresPermission(AdminPermission.class) + public static class ConfigureQueryImportAction extends FormViewAction + { + @Override + public void addNavTrail(NavTree root) + { + root.addChild("Query-based specimen import"); + } + + @Override + public void validateCommand(ConfigForm target, Errors errors) + { + } + + @Override + public ModelAndView getView(ConfigForm form, boolean reshow, BindException errors) + { + return new JspView<>("/org/labkey/specimen/view/configureQueryImport.jsp", form, errors); + } + + @Override + public boolean handlePost(ConfigForm form, BindException errors) + { + String QBSpecimenImportKey = QueryBasedSpecimenTransform.PROPERTY_MAP_KEY; + WritablePropertyMap props = PropertyManager.getWritableProperties(getContainer(), QBSpecimenImportKey, true); + form.setUserId(getUser().getUserId()); + Map valuesToPersist = form.getOptions(); + + if (!valuesToPersist.isEmpty()) + { + props.putAll(valuesToPersist); + props.save(); + return true; + } + + return false; + } + + @Override + public URLHelper getSuccessURL(ConfigForm configForm) + { + return PageFlowUtil.urlProvider(StudyUrls.class).getManageStudyURL(getContainer()); + } + } + + @RequiresPermission(AdminPermission.class) + public static class ReloadQueryBasedImportAction extends MutatingApiAction + { + @Override + public Object execute(ConfigForm form, BindException errors) throws Exception + { + ApiSimpleResponse response = new ApiSimpleResponse(); + + try + { + SpecimenTransform transform = SpecimenService.get().getSpecimenTransform(QueryBasedSpecimenTransform.NAME); + PipelineJob job = SpecimenService.get().createSpecimenReloadJob(getContainer(), getUser(), transform, getViewContext().getActionURL()); + + PipelineService.get().queueJob(job); + response.put("success", true); + } + catch (PipelineValidationException e) + { + throw new IOException(e); + } + return response; + } + } } diff --git a/specimen/src/org/labkey/specimen/importer/AbstractSpecimenTask.java b/specimen/src/org/labkey/specimen/importer/AbstractSpecimenTask.java index 3acf4e95..951b261b 100644 --- a/specimen/src/org/labkey/specimen/importer/AbstractSpecimenTask.java +++ b/specimen/src/org/labkey/specimen/importer/AbstractSpecimenTask.java @@ -132,22 +132,25 @@ public static void doImport(@Nullable FileLike inputFile, PipelineJob job, Simpl SpecimenImporter importer = new SpecimenImporter(ctx.getContainer(), ctx.getUser()); importer.process(specimenDir, merge, ctx, job, syncParticipantVisit); - // perform any tasks after the transform and import has been completed - String activeImporter = SpecimenService.get().getActiveSpecimenImporter(ctx.getContainer()); - if (null != activeImporter) - { - SpecimenTransform activeTransformer = SpecimenService.get().getSpecimenTransform(activeImporter); - if (activeTransformer != null && activeTransformer.getFileType().isType(inputFile)) - doPostTransform(activeTransformer, inputFile, job); - } - else + // if there's an inputFile, perform any tasks after the transform and import has been completed + if (inputFile != null) { - for (SpecimenTransform transformer : SpecimenService.get().getSpecimenTransforms(ctx.getContainer())) + String activeImporter = SpecimenService.get().getActiveSpecimenImporter(ctx.getContainer()); + if (null != activeImporter) { - if (transformer.getFileType().isType(inputFile)) + SpecimenTransform activeTransformer = SpecimenService.get().getSpecimenTransform(activeImporter); + if (activeTransformer != null && activeTransformer.getFileType().isType(inputFile)) + doPostTransform(activeTransformer, inputFile, job); + } + else + { + for (SpecimenTransform transformer : SpecimenService.get().getSpecimenTransforms(ctx.getContainer())) { - doPostTransform(transformer, inputFile, job); - break; + if (transformer.getFileType().isType(inputFile)) + { + doPostTransform(transformer, inputFile, job); + break; + } } } } diff --git a/specimen/src/org/labkey/specimen/importer/QueryBasedExport.java b/specimen/src/org/labkey/specimen/importer/QueryBasedExport.java new file mode 100644 index 00000000..7dde054b --- /dev/null +++ b/specimen/src/org/labkey/specimen/importer/QueryBasedExport.java @@ -0,0 +1,97 @@ +package org.labkey.specimen.importer; + +import org.labkey.api.action.NullSafeBindException; +import org.labkey.api.data.ColumnHeaderType; +import org.labkey.api.data.Container; +import org.labkey.api.data.ShowRows; +import org.labkey.api.data.TSVGridWriter; +import org.labkey.api.data.TSVWriter; +import org.labkey.api.pipeline.PipelineJob; +import org.labkey.api.pipeline.PipelineJobException; +import org.labkey.api.query.QueryService; +import org.labkey.api.query.QuerySettings; +import org.labkey.api.query.QueryView; +import org.labkey.api.query.UserSchema; +import org.labkey.api.security.User; +import org.labkey.api.view.ActionURL; +import org.labkey.api.view.ViewContext; +import org.labkey.specimen.actions.SpecimenController.ConfigureQueryImportAction; +import org.labkey.vfs.FileLike; +import org.springframework.validation.BindException; +import org.springframework.validation.ObjectError; + +import java.io.IOException; +import java.util.List; + +public class QueryBasedExport +{ + private final QueryBasedSpecimenReloadConfig _config; + private final PipelineJob _job; + private final FileLike _archive; + + public QueryBasedExport (QueryBasedSpecimenReloadConfig config, PipelineJob job, FileLike archive) + { + _config = config; + _job = job; + _archive = archive; + } + + public QueryBasedSpecimenReloadConfig getConfig() + { + return _config; + } + + public void exportRepository() throws PipelineJobException + { + _job.info("Starting query-based specimen export"); + + User user = _job.getUser(); + Container c = _job.getContainer(); + String schemaName = _config.getSchemaName(); + String queryName = _config.getQueryName(); + String viewName = _config.getViewName(); + + // We fake up a ViewContext here as we're in a background job, and and no request-based ViewContext is available + try (ViewContext.StackResetter reset = ViewContext.pushMockViewContext(user, c, new ActionURL(ConfigureQueryImportAction.class, c))) + { + ViewContext viewContext = reset.getContext(); + + UserSchema userSchema = QueryService.get().getUserSchema(user, c, schemaName); + if (null == userSchema) + throw new PipelineJobException(String.format("Schema %s is either inaccessible or deleted.", schemaName)); + + QuerySettings querySettings = new QuerySettings(viewContext, "query", queryName); + querySettings.setSchemaName(schemaName); + querySettings.setViewName(viewName); + querySettings.setShowRows(ShowRows.ALL); + + _job.info(String.format("Requesting data from query '%s' of schema '%s'", queryName, schemaName)); + BindException errors = new NullSafeBindException(new Object(), "form"); + QueryView queryView = userSchema.createView(viewContext, querySettings, errors); + + if (errors.hasErrors()) + { + List allErrors = errors.getAllErrors(); + for (ObjectError error : allErrors) + { + _job.error(error.toString()); + } + return; + } + + _job.info("creating the exported data .csv file"); + try (TSVGridWriter tsvWriter = queryView.getTsvWriter()) + { + tsvWriter.setDelimiterCharacter(TSVWriter.DELIM.COMMA); + tsvWriter.setColumnHeaderType(ColumnHeaderType.DisplayFieldKey); + tsvWriter.write(_archive.openOutputStream()); + _job.info("finished writing data file: " + _archive.getName()); + } + catch (IOException e) + { + _job.error("Error writing TSV: " + e.getMessage()); + } + } + _job.info("Finished writing data file: " + _archive.getName()); // use Format + } +} diff --git a/specimen/src/org/labkey/specimen/importer/QueryBasedSpecimenImportUploadTask.java b/specimen/src/org/labkey/specimen/importer/QueryBasedSpecimenImportUploadTask.java new file mode 100644 index 00000000..af9b7f64 --- /dev/null +++ b/specimen/src/org/labkey/specimen/importer/QueryBasedSpecimenImportUploadTask.java @@ -0,0 +1,81 @@ +package org.labkey.specimen.importer; + +import org.apache.logging.log4j.Logger; +import org.labkey.api.data.Container; +import org.labkey.api.data.PropertyManager; +import org.labkey.api.module.ModuleLoader; +import org.labkey.api.pipeline.PipelineJob; +import org.labkey.api.pipeline.PipelineService; +import org.labkey.api.security.User; +import org.labkey.api.security.UserManager; +import org.labkey.api.study.SpecimenService; +import org.labkey.api.study.SpecimenTransform; +import org.labkey.api.study.StudyService; +import org.labkey.api.util.SystemMaintenance; +import org.labkey.specimen.SpecimenModule; + +import java.util.stream.Stream; + +public class QueryBasedSpecimenImportUploadTask implements SystemMaintenance.MaintenanceTask +{ + @Override + public String getDescription() + { + return "Query-Based Specimen Import Upload Task"; + } + + @Override + public String getName() + { + return "QueryBasedSpecimenUpload"; + } + + @Override + public void run(Logger log) + { + String QBSpecimenImportKey = QueryBasedSpecimenTransform.PROPERTY_MAP_KEY; + try (Stream stream = PropertyManager.getNormalStore().streamMatchingContainers(PropertyManager.SHARED_USER, QBSpecimenImportKey)) + { + stream.forEach(c -> { + try + { + // Study must have been deleted + if (null == StudyService.get().getStudy(c)) + { + log.error("Query-based specimen import failed: Study does not exist in folder {}", c.getPath()); + return; + } + + if (!c.getActiveModules().contains(ModuleLoader.getInstance().getModule(SpecimenModule.class))) + return; + + SpecimenTransform transform = SpecimenService.get().getSpecimenTransform(QueryBasedSpecimenTransform.NAME); + PropertyManager.PropertyMap props = PropertyManager.getProperties(c, QBSpecimenImportKey); + boolean enabled = ("on").equals(props.get("enabled")); + + if (!enabled) + { + log.info("Prohibiting queuing specimen import for {}. Query-based specimen import is not enabled.", c.getName()); + return; + } + if (!transform.isActive(c)) + { + log.info("Prohibiting queuing specimen import for {}. Query-based specimen import is not the active import mechanism.", c.getName()); + return; + } + log.info("Queuing specimen import for {}", c.getName()); + + int userId = Integer.parseInt(props.get("userId")); + User reloadUser = UserManager.getUser(userId); + PipelineJob job = SpecimenService.get().createSpecimenReloadJob(c, reloadUser, transform, null); + + PipelineService.get().queueJob(job); + } + catch (Exception e) + { + log.error("Query-based specimen import failed", e); + } + }); + } + } +} diff --git a/specimen/src/org/labkey/specimen/importer/QueryBasedSpecimenReloadConfig.java b/specimen/src/org/labkey/specimen/importer/QueryBasedSpecimenReloadConfig.java new file mode 100644 index 00000000..396d63c6 --- /dev/null +++ b/specimen/src/org/labkey/specimen/importer/QueryBasedSpecimenReloadConfig.java @@ -0,0 +1,58 @@ +package org.labkey.specimen.importer; + +import org.labkey.api.study.SpecimenTransform; + +public class QueryBasedSpecimenReloadConfig implements SpecimenTransform.ExternalImportConfig +{ + private String schemaName; + private String queryName; + private String viewName; + + public String getSchemaName() + { + return schemaName; + } + + public void setSchemaName(String schemaName) + { + this.schemaName = schemaName; + } + + public String getQueryName() + { + return queryName; + } + + public void setQueryName(String queryName) + { + this.queryName = queryName; + } + + public String getViewName() + { + return viewName; + } + + public void setViewName(String viewName) + { + this.viewName = viewName; + } + + @Override + public String getBaseServerUrl() + { + return null; + } + + @Override + public String getUsername() + { + return null; + } + + @Override + public String getPassword() + { + return null; + } +} diff --git a/specimen/src/org/labkey/specimen/importer/QueryBasedSpecimenTransform.java b/specimen/src/org/labkey/specimen/importer/QueryBasedSpecimenTransform.java new file mode 100644 index 00000000..1e92c7b5 --- /dev/null +++ b/specimen/src/org/labkey/specimen/importer/QueryBasedSpecimenTransform.java @@ -0,0 +1,128 @@ +package org.labkey.specimen.importer; + +import org.jetbrains.annotations.Nullable; +import org.labkey.api.data.Container; +import org.labkey.api.data.PropertyManager; +import org.labkey.api.data.TSVMapWriter; +import org.labkey.api.module.ModuleLoader; +import org.labkey.api.pipeline.PipelineJob; +import org.labkey.api.pipeline.PipelineJobException; +import org.labkey.api.security.User; +import org.labkey.api.study.SpecimenService; +import org.labkey.api.study.SpecimenTransform; +import org.labkey.api.util.FileType; +import org.labkey.api.view.ActionURL; +import org.labkey.specimen.SpecimenModule; +import org.labkey.specimen.actions.SpecimenController.ConfigureQueryImportAction; +import org.labkey.vfs.FileLike; + +import java.util.ArrayList; +import java.util.Map; + +public class QueryBasedSpecimenTransform implements SpecimenTransform +{ + public static final String NAME = "QueryBased"; + public static final String PROPERTY_MAP_KEY = "queryBasedSpecimenLoader"; + + @Override + public String getName() + { + return NAME; + } + + @Override + public boolean isValid(Container container) + { + return container.getActiveModules().contains(ModuleLoader.getInstance().getModule(SpecimenModule.class)); + } + + @Override + public boolean isActive(Container container) + { + PropertyManager.PropertyMap props = PropertyManager.getProperties(container, PROPERTY_MAP_KEY); + boolean enabled = ("on").equals(props.get("enabled")); + // If selected is null, the container has only one transform + String selected = SpecimenService.get().getActiveSpecimenImporter(container); + + return enabled && (selected == null || NAME.equals(selected)); + } + + @Override + public FileType getFileType() + { + return new FileType(".qbst.csv"); + } + + protected static class QueryBasedTSVWriter extends TSVMapWriter + { + private int _rowCount; + public QueryBasedTSVWriter(Iterable> rows) + { + super(new ArrayList<>(), rows); // start with empty column list + } + + public int getRowCount() + { + return _rowCount; + } + + @Override + public void writeRow(Map row) + { + if (_rowCount == 0) + { + writeFileHeader(); + setColumns(row.keySet()); + writeColumnHeaders(); + } + super.writeRow(row); + _rowCount++; + } + } + + @Override + public void transform(@Nullable PipelineJob job, FileLike input, FileLike output) throws PipelineJobException + { + QueryBasedTransformTask task = new QueryBasedTransformTask(job); + task.transform(input, output); + } + + @Override + public void postTransform(@Nullable PipelineJob job, FileLike input, FileLike outputArchive) + { + // noop + } + + @Override + public @Nullable ActionURL getManageAction(Container c, User user) + { + return new ActionURL(ConfigureQueryImportAction.class, c); + } + + @Override + public ExternalImportConfig getExternalImportConfig(Container c, User user) + { + Map props = PropertyManager.getProperties(c, PROPERTY_MAP_KEY); + QueryBasedSpecimenReloadConfig config = new QueryBasedSpecimenReloadConfig(); + + String schemaName = props.get("schemaName"); + String queryName = props.get("queryName"); + String viewName = props.get("viewName"); + + config.setSchemaName(schemaName); + config.setQueryName(queryName); + config.setViewName(viewName); + + return config; + } + + @Override + public void importFromExternalSource(@Nullable PipelineJob job, ExternalImportConfig importConfig, FileLike inputArchive) throws PipelineJobException + { + if (importConfig instanceof QueryBasedSpecimenReloadConfig queryConfig) + { + QueryBasedExport export = new QueryBasedExport(queryConfig, job, inputArchive); + export.exportRepository(); + } + } +} diff --git a/specimen/src/org/labkey/specimen/importer/QueryBasedTransformTask.java b/specimen/src/org/labkey/specimen/importer/QueryBasedTransformTask.java new file mode 100644 index 00000000..90a61d1c --- /dev/null +++ b/specimen/src/org/labkey/specimen/importer/QueryBasedTransformTask.java @@ -0,0 +1,160 @@ +package org.labkey.specimen.importer; + +import org.apache.commons.lang3.StringUtils; +import org.jetbrains.annotations.Nullable; +import org.labkey.api.collections.CaseInsensitiveHashSet; +import org.labkey.api.pipeline.AbstractSpecimenTransformTask; +import org.labkey.api.pipeline.PipelineJob; +import org.labkey.api.pipeline.PipelineJobException; +import org.labkey.api.reader.DataLoader; +import org.labkey.api.reader.DataLoaderFactory; +import org.labkey.api.writer.PrintWriters; +import org.labkey.vfs.FileLike; + +import java.io.IOException; +import java.io.PrintWriter; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +public class QueryBasedTransformTask extends AbstractSpecimenTransformTask +{ + public QueryBasedTransformTask(@Nullable PipelineJob job) + { + super(job); + } + + private final Map _primaryIds = new LinkedHashMap<>(); + private final Map _derivativeIds = new LinkedHashMap<>(); + private final Map _additiveIds = new LinkedHashMap<>(); + + @Nullable + private String getPrimaryType(Map row) + { + String colNameLK = getNonNullValue(row, "primary_type"); + return ("".equals(colNameLK)) ? getNonNullValue(row, "PrimaryType") : colNameLK; + } + + @Nullable + private String getDerivative(Map row) + { + String colNameLK = getNonNullValue(row, "derivative_type"); + return ("".equals(colNameLK)) ? getNonNullValue(row, "DerivativeType") : colNameLK; + } + + @Nullable + private String getAdditive(Map row) + { + String colNameLK = getNonNullValue(row, "additive_type"); + return ("".equals(colNameLK)) ? getNonNullValue(row, "AdditiveType") : colNameLK; + } + + private Integer getType(String type, Map typeMap) + { + Integer id = typeMap.get(type); + if (id == null && !StringUtils.isEmpty(type)) + { + id = typeMap.size() + 1; + typeMap.put(type, id); + } + return id; + } + + public void transform(FileLike input, FileLike output) throws PipelineJobException + { + info("Starting to transform input file " + input + " to output file " + output); + + try + { + DataLoaderFactory df = DataLoader.get().findFactory(input, null); + if (null == df) + throw new PipelineJobException("Unable to create a data loader factory for the file: " + input.getName()); + DataLoader loader = df.createLoader(input.openInputStream(), true, _job.getContainer()); + loader.setInferTypes(false); + + try (ZipOutputStream zOut = new ZipOutputStream(output.openOutputStream())) + { + zOut.putNextEntry(new ZipEntry("specimens.tsv")); + PrintWriter writer = PrintWriters.getPrintWriter(zOut); + final QueryBasedSpecimenTransform.QueryBasedTSVWriter tsvWriter = new QueryBasedSpecimenTransform.QueryBasedTSVWriter(Collections.emptyList()); + + tsvWriter.setFileHeader(Collections.singletonList("# " + "specimens")); + tsvWriter.setPrintWriter(writer); + try + { + loader.forEach(row -> { + if (MapFilter.getMapFilter(this).test(row)) + { + if (!row.containsKey("records_id")) + row.put("record_id", tsvWriter.getRowCount()); + + row.put("primary_specimen_type_id", getType(getPrimaryType(row), _primaryIds)); + row.put("derivative_type_id", getType(getDerivative(row), _derivativeIds)); + row.put("additive_type_id", getType(getAdditive(row), _additiveIds)); + + tsvWriter.writeRow(row); + } + }); + } + finally + { + writer.flush(); + zOut.closeEntry(); + } + + if (tsvWriter.getRowCount() > 0) + info("After removing duplicates, there are " + tsvWriter.getRowCount() + " rows of data"); + else + throw new PipelineJobException("There are no rows of data"); + + writePrimaries(getPrimaryIds(), zOut); + writeDerivatives(getDerivativeIds(), zOut); + writeAdditives(getAdditiveIds(), zOut); + } + } + + catch (IOException e) + { + throw new PipelineJobException(e); + } + } + + @Override + protected Map transformRow(Map inputRow, int rowIndex, Map labIds, Map primaryIds, Map derivativeIds) + { + return null; + } + + @Override + protected Set getIgnoredHashColumns() + { + return new CaseInsensitiveHashSet(); + } + + @Override + protected Map getLabIds() + { + return new LinkedHashMap<>(); + } + + @Override + protected Map getPrimaryIds() + { + return _primaryIds; + } + + @Override + protected Map getDerivativeIds() + { + return _derivativeIds; + } + + @Override + protected Map getAdditiveIds() + { + return _additiveIds; + } +} diff --git a/specimen/src/org/labkey/specimen/view/chooseImporter.jsp b/specimen/src/org/labkey/specimen/view/chooseImporter.jsp index 6cc91050..0fc794dd 100644 --- a/specimen/src/org/labkey/specimen/view/chooseImporter.jsp +++ b/specimen/src/org/labkey/specimen/view/chooseImporter.jsp @@ -5,9 +5,7 @@ */ %> <%@ page import="com.google.common.collect.Iterables" %> -<%@ page import="org.labkey.api.admin.AdminUrls" %> <%@ 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.study.SpecimenService" %> <%@ page import="org.labkey.api.study.SpecimenTransform" %> @@ -18,7 +16,6 @@ <%@ page import="org.labkey.api.view.ActionURL" %> <%@ page import="org.labkey.specimen.actions.ShowUploadSpecimensAction" %> <%@ page import="java.util.Collection" %> -<%@ page import="static org.labkey.api.util.HtmlString.NBSP" %> <%@ page extends="org.labkey.api.jsp.JspBase" %> <%@ taglib prefix="labkey" uri="http://www.labkey.org/taglib" %> <% @@ -34,9 +31,6 @@ int rowNumber = 0; String selected = SpecimenService.get().getActiveSpecimenImporter(c); - HtmlString manageFoldersLink = h(urlProvider(AdminUrls.class).getFolderTypeURL(c)); - HtmlString labkeyEditionsLink = h("https://www.labkey.com/products-services/labkey-server/labkey-server-editions-feature-comparison/"); - HtmlString contactUsLink = h("https://www.labkey.com/about/contact/"); HtmlString manuallyImportSpecimensLink = h(urlFor(ShowUploadSpecimensAction.class)); %> @@ -49,6 +43,12 @@ +<% + // At the moment, there's exactly one SpecimenTransform, QueryBasedSpecimenTransform, which is provided by the + // Specimen module. As a result, this "choose importer" page is never linked (if a single transform exists, the + // "Configure Specimen Import" link navigates straight its configuration page. This probably won't ever change, + // but we'll leave this page in place just in case. +%>
<% if (numberOfTransforms > 1) { %>

@@ -114,8 +114,7 @@ %> - <% } else if (numberOfTransforms == 1) { %> - <% + <% } else if (numberOfTransforms == 1) { SpecimenTransform transform = Iterables.get(specimenTransforms, 0); ActionURL manageAction = transform.getManageAction(c, user); %> @@ -138,21 +137,6 @@ <% } else { %> - <% if (ModuleLoader.getInstance().hasModule("professional")) { %> -

- External Specimen Import is not currently available for this folder. - To use External import, > enable the Professional Module for this folder. -
- <% } else { %> -
-

<%=NBSP%>

- External Specimen Import is a Premium LabKey feature. > Learn more or - > contact LabKey . -
- <% - } - %> - > Import specimens manually <% } diff --git a/specimen/src/org/labkey/specimen/view/configureQueryImport.jsp b/specimen/src/org/labkey/specimen/view/configureQueryImport.jsp new file mode 100644 index 00000000..f62ceaea --- /dev/null +++ b/specimen/src/org/labkey/specimen/view/configureQueryImport.jsp @@ -0,0 +1,132 @@ +<% +/* + * Copyright (c) 2013-2016 LabKey Corporation. All rights reserved. No portion of this work may be reproduced in + * any form or by any electronic or mechanical means without written permission from LabKey Corporation. + */ +%> +<%@ page import="org.labkey.api.data.Container" %> +<%@ page import="org.labkey.api.data.PropertyManager" %> +<%@ page import="org.labkey.api.pipeline.PipelineStatusUrls" %> +<%@ page import="org.labkey.api.study.SpecimenService" %> +<%@ page import="org.labkey.api.study.StudyUrls" %> +<%@ page import="org.labkey.api.util.ButtonBuilder" %> +<%@ page import="org.labkey.api.util.InputBuilder" %> +<%@ page import="org.labkey.api.util.InputBuilder.Input" %> +<%@ page import="org.labkey.api.util.PageFlowUtil" %> +<%@ page import="org.labkey.api.util.SelectBuilder" %> +<%@ page import="org.labkey.specimen.actions.SpecimenController.ReloadQueryBasedImportAction" %> +<%@ page import="org.labkey.specimen.importer.QueryBasedSpecimenTransform" %> +<%@ page extends="org.labkey.api.jsp.JspBase" %> +<%@ taglib prefix="labkey" uri="http://www.labkey.org/taglib" %> + +<% + Container c = getContainer(); + String selected = SpecimenService.get().getActiveSpecimenImporter(c); + boolean active = selected == null || selected.equals(QueryBasedSpecimenTransform.NAME); + String QBSpecimenImportKey = QueryBasedSpecimenTransform.PROPERTY_MAP_KEY; + + PropertyManager.PropertyMap props = PropertyManager.getProperties(c, QBSpecimenImportKey); + + String schemaName = props.get("schemaName"); + String queryName = props.get("queryName"); + String viewName = props.get("viewName"); + boolean enabled = ("on").equals(props.get("enabled")); +%> + + + + + +
+

+ Specimen data can be automatically loaded from an existing query using the specimen import configuration + defined on this page. The schema you specify must already be defined as an external schema, and the query + and view selected must map and filter rows as expected by the LabKey specimen repository. +

+ +

+ Learn more about configuring <%=helpLink("querySpecimenImport", "Query-based specimen reload")%>. +

+ +
+ + +

Configure connection

+ +
+ <%=InputBuilder.checkbox() + .name("enabled") + .label("Enable reload") + .layout(Input.Layout.HORIZONTAL) + .checked(enabled) + .formGroup(true) + %> + + <%= new SelectBuilder() + .name("schemaName") + .id("schemaName") + .label("Schema") + .layout(Input.Layout.HORIZONTAL) + .formGroup(true) + %> + + <%= new SelectBuilder() + .name("queryName") + .id("queryName") + .label("Query") + .layout(Input.Layout.HORIZONTAL) + .formGroup(true) + %> + + <%= new SelectBuilder() + .name("viewName") + .id("viewName") + .label("View") + .layout(Input.Layout.HORIZONTAL) + .formGroup(true) + %> +
+
+ + <%= new ButtonBuilder("Cancel") + .href(PageFlowUtil.urlProvider(StudyUrls.class).getManageStudyURL(getContainer())) + .build() + %> + + <%= new ButtonBuilder("Save") + .submit(true) + .build() + %> + + <%= new ButtonBuilder("Reload Now") + .id("reloadNow") + .enabled(enabled && active) + .onClick("reloadNow();") + .build() + %> +
+ + +
diff --git a/specimen/test/sampledata/import/100_specimens.xlsx b/specimen/test/sampledata/import/100_specimens.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..75b54324a2ed3fa67c30763cb2b3fb5ee02f8bd1 GIT binary patch literal 20576 zcmeIaby!qu+dfQ}L+8*PN{lp!v~(i~NH@|9-8pnfNQa^zibzOzmxLnH!Vn@M-TAG- z9q#=+zxVk5e_uEbhJm@)wXQhNEAF+HhB7J|2@(bp77`K?1CsexuB|aL5>gTx5)v^I z7RqB8XD4?nCwG&FJ}y>JBW`a;2l^LiDC`ADD8T3c@9TfD1xix~)jA;r4~`WsWR|&< zW-BGJ1b&A065ZAmZ|h3wd1LW7&(7{TFZ!H7`Z?(np$ejrSwG(0A=^qP$C}Wno<=QD zY)D_5jxm)GPj~MoODj1z+08(IyMTmB@-EoKB;Gn3DZ{n4Q;S~~E~QjTX-i5b9=w!0 z-K$GD-%}5IWAIv}Z>D`gP4^q2@Ic8J!O|z zm}}L$ouw zQjLPqk#pfyZ=S5Wh|ulOz~a9CI^j0;pBB{qPDP(Em(d=aT$3CTt~KnpNfY$9F)#;2 z{#@u=yXUt#y7D+|Yxmh&5bqTR64LcGDw4)OU|FpLVLAX}O$9*REdZ7#ZdMLZ9&W_X z|AXWIVhsMKdTFw%dM5+~+g7-U>^mNxOCpd~_L5R)WOx`DtT>Bb9bd>yHP^~UP4JL1 z3{^g;Iq+g&W=<@Ay^rB&k-s#Sh~yqqjn_L^`kAW-=51EDXL7Ek@Ge5n@x$?>OnD_A zcF*R-+i&Y$KUeObXObJ+m8~Ec;eJGkP5g=)Ms`0l#Gpr2|C{+q8S=Qa_Rn{)@>-#S z^^}q9powQiYq%0oA}X7oGfDfP<~CDh{(TM%ho_V}+O{Hg<>q;AqKrN!mJV(Eve~V~ zm;PL8Iel7;{6r_bBl5j01&2WfANY3%bA7smush%O?evGn(!Kr$WT^iHN%r^khe)VM zNM8U5;RBZOcHr@HcC$BkcD6_4tx_E$=WGZ`K>mm8;0v{K>?E`_T}66>4^D5+N)(^6 zlIJXzEM1-ePBRmFQF20B%STnP%xJ?Rv@5Jct zDqa=YrMyrr)sseDNZ z+w0M+w&d3qv1s6=CSPjf;CMeu#*pgVPB6%#uFv_o$TIC)sDiEr?t#v*{rK~=wZ{ue zw;!oG7QZM(Ud$ploUbnEyFKO-sEw`o{#H^mLre8j7xW!A3d-8Y*wXKx-S)X1L8Lr~ zYHOo>mKGRFBc1okbYB$HS!R#A->JR?o|gvt)_;4_0zJ_bEr=pMxZ2=-cv6$si&H-* zZc#D4y-*{kclMteuGEyZOHlODbwvB?to6e&ASCuj4$^{$FX8C2b1gG$eIm0nr?Lj% z$DfNvS524kXg%c!;tc1-uzM)Tv-)G62$H5@&~3Q~e1UQu@slsOY@}W~)mfHTw3!r; z8f3u^401JcAIEbt^5c?|<1F)hHgr3D>m`=wuNuf{UZiXAC6?{#<$Iw{3E|Wtxf;}l z+S!_89w-ut)nxsStn#g`)!n-f7vsK38x@>QIn4|+>~`iK3xgZ9#k!9ihzMq=lA`Zt zu>|;i?+qNKi$A|KJwdu{Nl+9jC^|nrZ7SEde@%2ICu9`9P!6A3Iy?40u2Q{zv1X1h zS0XabE%NxS{G*KL)%A-ytX~N$=iq(l_r1=s&0!mY{@r}JVHI+dGa~_J7p$1WU36KB zJFRfC=jT!orZBtBsebwd26PJt{N@s8{ELc6n=MX;D%Zh&AL%#ur9up69_wtJ@j>4Z z3BOTw-n(_o%WvQ3^ObUT%~;v)Y3QKpgt##CBn^1Zp)fJc$qzz->D$%0giPxrsCL%! zGvrCZgxAj!^2TThg+49YzeDEF1cMCJdjRDAcYl>?sVa9t z2%2wQMMAu?JxK|t+<6!`bvE#^`af{Yurlxk9?VnSZZOjOs?3Ax4D;C@>h(Mpz*|Hm zK5W19I)(_%m)K$Z9=h|;{tzmb`OkN>q4Ma&M4RgyZ-3sx%YqWuizoMS=8*`0KVfG9 zM-~<_${y5zhVefchIsHkfsWD)00cYuVLA9LTzm6f=!e=``YaCXmFs$$nInw+P#?Na z`(SKEFhIGqf4~rAv)*xgg3g8oe@GI2T%E}$e zbMtcdhG=sgoo98EBU*XNuiJNyyUc5i!@6Q^o2+f*G_~i5hdEn05{RfQ9_uSAT9;2= zx2OtCHR-G}Y?w=#+B^-HFOKmz|70~Up4@Vk7!-JRvUuDW*^7Ui-|D09?{l!%J-6z0 zED>;WF(||nbh0tSGi=i0?cZ>DG4`|fe8g+M>0)ATY4F-fNY zb?0<9JAFGpFYt1G{B5t-_O|$(c%aYq#@y;$TK+a2@b2f;Aw2z2VypP<+||LwgM9H8 z18SPwmNWNr`2JvX=OcU(+GC093;5hz_PJMUK*RaP2O*!jbMNyH8`ry+ktTG0wd&h$ zo4}DSmTFB}y}hp2CUUzI>BIxQ&nBwY2Fdfk1su-t0K%MtF6tV$gUEVow~a6M&5na& z=dPaxWUgLwr-=u6AG=(8U+<5l-&=ijo_Bb}V{Fva5a8wV`Fy03Bf;eU?B&&Sqqg=Z zyVv$5!Xn;W7*#9RW19!2UlTUqZ|B7?4u%G=eV}KKyWcOCf5djXRh%rIxcMA)*RHP4 z2fF%Ko{V)*&8(b$Iz7E^@xMG&r}Mls`)xcnvJo)xspGDWHZ@JN|KZuj>LZOy_*P=? za=wY!+*Mukx%ctbn$x3K-ls1DLaw}nt`l`n=C&kgeu~lMqrY2S3OX(e3OX;^5Ac8l zULNj@1U#)BdB404?^gGcn7=yOwS!*wu5WenZ0CE9t9c$zv`Ds`t(iD)(a!%g&TRBG z;Z~b=nx6}r)!)3jj#Z~K=<=*OaX%lql(^W17Z)El28mtmEpOWDZ?-ISH_p@MZ%JIf zYaFF)n!YC{bo~8j?dWg<|2U{=vf02>==5a6anN{oeDPo6&?)H|M#&zQj zv&v*6ZRGOgPQgJan6(+sT9H+Siyw`y)gQwT`raMPEck*AO=evCWG2O@UyazQ6r-D_5Q=JaKv!jeY$c2eD=3)RiaXqK5km!Df-o#=xi-n-IY z63l$1cU3+TN~(KseNSQ5tMWQ#qB_&?k<6x(M1JJt*FK`ggRU)>!mc-+9mLt91Qj)* zL{x@6VH1d6Cu`{;{;v{xHTMZ_RQPFfW1=vE|Jg!si5LW*m0BpGD{mPO3snrVK65y4 zr-_PqzuVPQBcLEw@kWZ@6!Dh=;Jr4qNZA`|mx*FzT8&)m> zX~vl5V}p{Vu59LJD0zB-b#)lKdXhajOHBLcK6U?YAF+xGM_eLx&R+t0Hl|g|-ZIo& z+Il`42{K@kFTDq=`~PVk=^F>&Je#fdBW`ps@2{bo4beNHRCjQA90mlyFB!WHf+-tM zqM9>ef$$;RP|rI`)-)fNic+PC(t9iwja!!9eyHP16Q#<7TG5jm6@h&d7&_4eR0z{+ z^+!wi#25)ySkB;YNpkwfvkBI``xQ7q5KMX-W%qjsbdfbmeoXlQ_cmbP zjbwW3Y-?#*MoZ?k0B!iYJ1=JCLV1Zw;F;3CVZ8s;w>I zOTmo*gf#6pzohAfAY0en3K3};%X)L`;{p>TOwKgnQGH0DoS737<$hbKSt4~~S@Ne`kweK0N5Wtcg48nAU=a`cjIj;0sx>6DgfW3f{9GX-Y9OPqL`;v| z=OvQRMf`GJe?f58PqS!@47l1QswYCP5D_M+2jVDDIoK25w4cMXMMB?0^r7@@SCF?u zrrWgLyRG(8mx(B`V)~xe8sp2Zv%b^qL zHW7Vr=J-?A^LzjD5L9yKeAwnRN8tXq>h&?OhJs=Ou_CF!W=qwq0-uwWpy?vjH(F~U zQ_xTB9={<7=A(up5v)#d=IuQ~7&2*X9qvh-Q=em=1uaYf>~`BZ?L7G7Bn-4hWF~wl z33DVg6(&f1K(7ZzzMDfy&{>4UTbiN)W2+LT>GT^y>m<14mh--GCO7Z{Y$g4;zXfbY zp<>84)47QWgOi2B3*1$Y*+DW4KCdz2A=ErhsF!iPv#es@&Gf1mi}&#A6F9eAS6UEDJD>F?2M#DA*ePp(TC#eL0?JJ-7Oh@3y=)T0}8eK zNoJ4HDDcp%2(fhKBtgX(UF96Xpbh3!cz=^x?~XBFJ47}z9ImJB!E_fi*l0c<2LZij zFssX-YlD1X17W_h|6u}#Nq0bO!{OepR6lG|;ezPA_?=m7B5B8+0j1V)W_>TQI!iEl z9b%C>Lkc)i+6SIGWG8fh;zS*-={J>L*%Rlz z(#A9wZdL;$yRMD;k$~?xBFq#z255?X1CqP-6`+5|qXTQ>fuanQ0?|zbdkKLR^oh zFIzU;L(Qy?+9p=Z|a{2OFf(vk9AdYBQRl`v82Q&TInw(@#B&TWfY)orW4R|I9gx@>KKMe>XCclZkaanMJ z04Z1NbsAN!^@TpAN-8|i6SZ?R53;$skpJrWha9`7auhJ@o1n;AofH>)Y-;};p2Fw# z+I7mV2Mj}sg;1l{7!sOJV$|?25sDzpR};e3?>&jqSFR=vhDG1HftBkEdtBu^pOg&( z^LKyn6NBve23#a&Lf~<)bsshq`K1O3GZjA&?{I0V!ySUg!Kd5|ZsF$VGz#ga=UV5$ z<8q$uwgm_e*wu3f!`sZPs1cEOgzyne@g#>#MNH&qGT$ylE=b>qy3@essz{>>aLObWZD@F7Rw{Gy#4?qpYOl> zgOOh#7}@7T!uE%vbrimnzA*t?wC~<&0H`)!HeAkq;%hH;O@(@erH_oW5`CFFJb1+> zfoCR0U`-q=ijsjUwKk2Gp^X_T(yRu_#v`!K{Vr{a5#I)qVx)TkE7{0io-9Vmhl>1& z2@nKdI^Zkww}FOHpoYyi!hrK5%vjQKii&)CYB-pc0g*mnvQOKn?Z3_`6nUQD@UOZplKwK}GuvQr z4*K^xU)6DR>m9-jDJDqo+gX5;?m?;UWUk(QjCy<3ZOe*R1&Qxv3flKCeVCJQA^qkfl4<9OjFizG|1b?7Ms6BtlV&esTwYpwKkI3 zO(YOzFgP$ndb2#dddDE0cO>!J77eoF3}!lUK#;JiHtI+3OB&ih;wXH7`XBUt#o_LI z8@BW82JQ=Fy<+gSHYfNl&yp0Rkr4;V%+3TD3dtHuV_JibWt~w(9sIPX6GSO8G(1|$ z$4}af0<3|Ju*y{3SJ^@03rLF;>8xjskIdw!wf^j#!KYiFX>4^apEmuYpYz_nmJBEP zo=w_W>9E2UrPu-fomaG2Ra__#fD!C1#I}E{Fho@&{;DuwX;160eiMXn$ZSL#^}O9b zoK8T@R6I2}6>2h~=45k(X_{;8Z!?S)2*wW=>GNpzFtW8}2CAiCQ@l`<8l( zJy-%|5Hf9PTN6o$Cv0Qc#+zgR8EY5}hU^jM@bQ|C4LZ0wX|wN(+1ls565g{hRie-&LFKUGidfXAT{qV#{Yh34I9%+b z3$-Gd`BTozKjm=T$kF;G2g<&`P#XPgQ9-TU0ZG{vrWbeZ|0L6wez*p!$rT7*jtajA zXC~eCyeJ(a?>1V zzlrpY+rQx!4ohr;V#F^{^FaBUKa#BqM)TqiXW4NiEK`4!D<`qR>cJW=!CV9~_i#vo zAx3B#A;EA7GTZ@&e%Mv^$D0jv%2naBzl6Yp;M=Nl6^K$+FBI9vR?{WHxv*$Gl5%e#iuRdl8`yv(XKChjNy7q2JQTpo zs&{7gnpKT(tzD{*%U45X%|6WxY$>}yOKkY>a*U{m^R-^#g+qOFU4UYF*j436Oc4wI zjO)c;9(s{fSUzc+@@e#_Tn=@3fm8TTlP!QK+eM7OTl;2m4Lj0W(dGzDX_2^*SVMIi z?#YPBxd6zwd5q?Qhql6ts0v&F#@F@qNUjo%UDJ)mxKa(QOP=IxL6AYI{ zEmC6-C;%2~n=-`AaYXLOWQ9w3vc}KnN-@#hv$6dO&cZQ4xnk$+v_+Up&buwMWnd`> z$Y?!FDwK(Qc4EWe!C4ias_y!smme$d(b+e=V!zPTW25W^Gx7#{3DQRGf3rN$Jbkwz zE%7=AD8lJ;#>lX2#uyi=BO}U8m-p)VY)Mvy*{g?lN_JQl_{_j^2xyk0p$@xHg+l$` z3h0pMw9C8v7GqPqX1!U?95_KFU!!|mhp{5S_+2765%t_itB3@RwMtK7eNQrnPudLP z)-8hP4>S~F4nU*ij}2%KDnvvV1w0rkcbgNzVGB7=ahT}ZMVLqh5dl11*F9K`Ap57S-mq2zjb1d?InW%6Q!H;n+ZNRaf zA#%RUutLPsM1+8BW9!&`hmvv&g$S-ctRv04SDwpSMzG3G3UEqJ;X{B^a>5=0oZ<>B zYwsgnjeAIO!j6z}bK-I4HBe{ea|-2qy%a z2m>X=sxY_vxX0a7DN@@5cFqK)cYHv>LUI)ro&C!Z0L=g2`T)3?*E*paX%?$-z0(br z<_(3ehX`Y4merQs@}9(_*6j)T3mSkj05pVq@4mL*`80O}jUDxCA#nFi{|Hdl^9NJ8 zf5N=--e2H|g=Q;dV%iMVl3YD-^>NtOi{T5mL|grW>RR()!R#KQO}!)pA)*#1{7 zCFsWY{xPqpBf90X<6uC3r*OfrIsP<&4;~haQ4Z3~>?UVjSjI6NZhUH4qZcbti`88 z&7|`+Jo>Q@XqFP#*Yd_|+~~`3^c1I9ec+9m;7^n9;rBDSumStgmM31Guc-0kxyca$ z4A7@Z1TX--yduB=^q7hO1B8OwMbW`MEcDv~#Pq%3_X3<{p&%N%^92PPe&osq_P$GZ z%p^W(Q=n;b0(4ObzR{%#mm=Ox>@nIV8l!3Np4^o6+vV9?DhKgyB*+^oJwe|Xe>G-Z zt^~+c=t&k7#cm*EM!5I;%mraQuXkofsTRJ-eKVHx@nZUKW8G%ey%vO-F30COf7B-3 zh=~gPL(C`0-k_KI^k%=Ss zf(HQ~vG+PoTjpKrLGN@&Q@c-5q;2&Q1U|h4<|_$drC(=KByWNoq0w}rDrVo^IBgM_ zD|Rn1U+Hy4dNMb+eGsU^gCz1}Z>|qo-N4>^d4Q1sr?r&8Q<`v3>9|Xhd`ZFI;o7q& zI64Z5`e;d;AW*KA(KjlVqks_vq9%2^#n3XBbmJgAFFJ*n^2T{+z1 zBqP6i^zHJM_RHm*KFodySG)In5z|fiK-`Ffu;REl@)Hs^w~s50uCa~CZ*@yjWj*=^5v7w;gD-xN>2kgM8#XNmTD+X1{ zhX^riAt}a?t>6eI0cI`c8P7DDCR8*)VCG|j>MbWPU%sNlbQ$jXc#TqSC0B{25c4|B z=fdnpW2!ja8Vc+f@fy_c5hEl`f6>RUH>UanzPm4u)QNuj;b#GPTJ>Z}ilmWI`S~A{ z0?CScJdzm{*}E#z>_v7yF~Fpt+mzAI6{vO(kL3+N#}`u=hA(#R;E`wtoHHNR!um_dHQ=j`4FcB+LJrxI1#*0a- z`U~P}u7BzSXKp3$oE|_XFKd-pSeKO%lCJP9BsuP+VTbt<#9Y!FA^^q?la{_~8@}l6 z0d%_VJb7Gw9yt9Ztw`y)wmFM&^y#~jF-9O|j+YX0seAc%?`b`@?ef%Tk1CER=ztGj?OW@+5fw4jh= zrx;RX&O)1tc;If9iL@r?l&E`c7NnWzN9bGhDfTHdWIXB*lOnWs@~y zppqzy$NpV8nqsX|%2xm#PR24k{iT_STCG1261rM3=WtvSnILIbU~WmJ5%W9=VMk%( zIgB&#t2))EAFIe^XjeWK*Q%*L!9FsNV(KUMSPvnYVJs)d4E#Vq3AWv2{X+7V(c=(t1929vloaQ`Un(>Qnm4Ar^t{~0luI_@CAD@MtCJh zF#DJ=x)l-jfO!2fqFl|An+b4pK&3Z=T+@R?xy znL+Z5Y(J;pUZ%Bn4D^~6Se^p<#N5h8h0k1%f7Wz$c06%Vk+LDJ!GoAxL@j?nXPo*F z#oQ@85^hO^-7nt0d~B&F!UF!R7^vR!C^#s4HoAxIeftB&tzMxd&?R<%Ai>pI76t>e z3NjO%4wF;Bh))6vDO^?e)MY^EJukqI@~hfI{7|WOF`PLw8V5b1{$pNP)X!M_!I7LI0WuPFgcB3y#<(@Xp9?`KqjUY?mH54 zw`Hqj)nGu>KndmxK-54;f!HlXdz-3_Dm_IanQE33!yAoeMdwJA%8<;=j@W34d7z&F zs1#bMtMw3=FklHJm~YrHoAcR z#|M}`jua5)F?cdRMhsDz6kEN16l)$3?F9Yg=(I)6T9o$QvJu_^9W#ja9vf)Kb^x;* z#DXOcr-J;7d^PcWuARfpWfT+gt#Oa3XgLc;Q${f={KY9!%{-=?dJ0J_d!wz2OHyo% zS#0Fxu2`7d(JE+2R7P7Wc;Ajw-deh-*Gmp4^PEGf4kd!(3sK*rsf9dTIzZ^?KkY*!bNMcC{R=1DP$^={0L z8b*55h}D8qeZ0T&4=(np!uMvw^A3C(8(SKCcXxoYBhQg=G9QtozpOek0XD?>WkXk- z8yiL~=hvF8Cog>*efYswe(OQt@8Wd46IL|=`ynQO{^6_Mxl~yj6eEsARQB2q)71nd{%$I6!&-t3s zW@o+u-8Zgn@M<6ob;O0P1M2@?pqgDQEOGNMPklNbK@6ty1g0v5_g2kTS&`7i!lHM8 z?W&O)tQ~4Br3uy@09GSw9*Y31#5$S7fhKf>K#3^NVyJ?y@g{RlP0q z!AKG6pDu#gChz>(Bk+L*;)vkK#yS>I{PIz0#A=hWzrl^qZzT*P=4NdGJK@kJjy zt6+v?5jl~W5E8&-9l#txY=*H7=(|S{eRmYlcY6c7i8dMGh;`sS#g1P)1Vezimc=-^ zYeLN1pc0)S0EqH#K(qrAN7xqO7GT42Rj_~;e}vLQpTv#Unn<&aF9U0&1al9dg%N-j z@Da3-r;WM@Aofq6C~(~PWNsH>sigh#x=D-}U{J^0EjMog5Q+9^anX0@KLMuaT-4Fj7UOT+rV$v*Z%9S?=tU_a5$xe44nE z^AtbZ*yctDn;_VY3Pu@*sVzZ39nxYGzj{9&5m{|l2oM$pUOAw30oQ9B*^D9YQLnMCH+&JBS%>S4;Z%OQnzzO?Eh59d-$W{E}lg@p? z19pJ<5zGbbKkf_OU?h7D!IQfHPr?e6_c*2+$?@l#nwlW>|0CW-hH;HN9M$%Jg|QDX zykGC7nzJOC3C8%9WA5XO>;`=K^5SnW{w`=xYq(Q|8^;^t7f>7t7l7RX@(^I-y9@e6 zQY;3K;->~v&&7a5iTX2g5c=MTS(0E^5EaVdVsB0oMH?0>^=yP`s48xB5BOC0+< zASv=|zjSV%{}558I+?GHxd`l`iUA$js<4pzc(usTZ%(1RUiEP1jd7Gj6+7mm_J+|$ zte%KJ`JEa8Fh2eh@m3tGW`YeYr{%XGgn7X50VwG6P^G4-?_QB-#|L`@6)1um5w`mD zVfpemG5N1@BD>|NASij~U_7y~>K*Fz_ja_u(Mr+8)ZT25_IEfqlv_C;zp=di&dOhi z|CRd9f__UUOS7Xo6Wx&Fyelt5>qa*-WB_-$ba}lP>fu@3cc${mz&{x z(C)6)d&)~JN=LD=uMq`D9;xdZny{EO9i&NJB+Ew1a6z>$n{JI|A$ual24?vW!-P7M zV-@&uD|(I!FQf1~#UKe=w0k*ZPe^<|YA%b&KgGOnGoEcae*C)C_vV_|U)M~we!Frz zzWmwALnkiLa^iiwzoiW?zy?j4%?ct?yymUD>c3FHi|80Km%Dwj9 zxcHgdF?eR=_*nh2brW{Eo_HP^BsSr58T4PT9&RTmXi>vNLh7MM+*x>Y^{~6Gm7^8U z&Ci<)hd1?yVyT2kTFK8OKv1s>fu%&2Zwn(1DRa!0xvA9EHJh5ce7wm`RKzGOB8OV+ zFJGlgJKsXoc-`dW-Wyc_u&5jnJ_lt9KIhRMz47 zGde|)ncHODSg@ycZPvme^Q>{b3LQ-OWWCfhVc#;y?!3}^N5PbQVjOC&;TOQmz_f{* zrWE8?(xeW*`&1e?j(z^on+lfa6-%9!(oqpVw6{~wXL_tK1HV@$sILus4>v9iT5CQRBir(a zrWMspif*JiC7I!BoQ*$ke6+1INcL^i#HgyC=1pS6M8U(Lau^+`Vd258_fj0fp zt)j;$v|ttcztAS?HQ!8N(uSh7qxv);;9FRnj?mFCKDL z#1vBl;eDBAWE!nUrFG;0gEAE%urmG#xPm7OqI zw6(X!6HTr!FUh}|v|jD3?+UGbVcYHKYP~oeOmDs36+J9poF(OH3E10POCg^RI9ydv zq?w+frpef0h_2WvgIjE~#+rrTo$n$`k@HkKgyVWHJNJ0}>cjveGkX%dNSoB_exQfTktZa+%jHs9m15 zjE7m)H-=;X%z`sq`MYT3A*EC+4rmGQoYtsIFj?8{$LIQ33cjcJJtyNe#GunE$$edc ztauC$o_d8TlKUr4-IKj9L#rN~L%DhII~eOp&dU z-`IcUa7)L!V|Bcn>R4{$>>BD##vV;L|MHYlc23DicqQkdZ*Bsf>Vg2u3f*$-7{>(q z4@h!jtKvpHmcC88FWHvAk;|Px=c-;)lW+HbwxEIipMdF)YLA8pH0?vh%wSkJAo97; zDAFrK+1$4V4qK_&li7m!s3dZ_&BQ6o@!ccQm24MY1GHYqPz?0M$GhjLqS@?LtFj?g z*vE97_P(}xvu!TPrO>ZFI#A>Blk4+0)?>!v@2!UIGxrU84v!9{o@6%phYYZ8@BPSA zZ>m_~V@TZ^MDK2t_6k62`V@x=c|D`qz*heRd6@AeOCW=DHkFAB*vgS$CX-1Tvu=)j-;gC zD@O|tnpYyFrlnxk)Tyf$q{SaHo8roFhBM`SQ}{|Uku}VD_~;YewWk_feV%yOy=i{% zl8Y(My(%8^84^kk=e#%c>|DR6Av5v|uX??CJ(U~ZdOSX%{R2j}x)4@Y?a{7#PN&sR zuxf8J@!Ovg|Dfoc@W!ZT`tq3kg?61MSTWD(#LMg%n7lGlHdWLxMQs4%?$Zpv)GjU) zuAnqr{!i2|9Pc~|CJ;AR%96Z8m+ea6ey`7)x1GrjHb~hklE2mzIrUSMUO);NEQWKa&V>LWq zWLV~P2t=2C{G@(|jzWDZtNr-~w^+s-)T3tgIU&070VsyucblT_+v{K4Q`REyE#to3 zllfA4`<2tB8kbv{J$F4G(YTLN6Z}g+ENVN1EB_;}s(zDNXG~R<=FA5^PAri67r_%p z4>{rcyJc(=eYqbEJR~_clyXnb24x52 z(T~TPZxUhSx(?)Uz?@T>Gx#7CUZTtn(r<#U%V;g74 zhGwG=Jni21PH=B6t#A!R5XQ!;n0zF!hMz6A7*6@SZWGeyo>vbP=sS8oiLkD|&yLot zRyex(RW!o*-Q#Us-?{Qn)M7i$Ot^T{<4zhILO9iKwU4o$uvi5a>c7TJQ@Z!Mmg=(L zlKzf`=?X`HWed{rlg66s|9W#|juS;27jSdrIB=go?yvL zQd+jq zkfdwC;uK@00j*s450j3R1Ri#z8`w#KI1j%$Q>iMBPpgKWYZ7q!OJ#JSb{M3l2V=cx zb&r2^`x8N@XB;2YU5_H#?b4GcjrAVh!*d8L%X()-g2ww|nViFPDB7LsCpwO0NE44d zhLq0k%%!#NDf-BvEUz*|MAOM8nr|QSJvBEO!?a%M^f9<&XYlZi8YKa7)HF$HtHPlU z*-?rBp0Uq#pbUO^7)w`R-m{oH@gN1vOemQm8jY9}md(AP(#NW3OTF#X`eE9B2gD#Z z=lGVDyLem<<~jL#MvqZU`?QlrG`Mc*6}SclIV^Z%rwO(Rw@P?#3y8op-A-Y;uDbeU z=TzkUDWpH`(%@;`lkvKr!2;GD`FE=|MRc8l7d(|7jf!i3W@fomlMAEFfY)_CA zATt4@vOFs$W%6Ueu0752{OBb;BNb^iNnIer8lq;{*@o_rNTFWWH}C5DklPJ&fOskP zB61-w!4X8HrK>f(?ckEXpY+!CZ>pE**^zQgpy3q3;F@s4}(hP z`xPzO;UV_*r!DV8=C@dde0OF%mpo;&V004h=JFo!EvbpL;U|MvP1AL^DK~zibhJwB zYHdQU=-|Q;k|~)xiNIcQ)Mi==9A8k||5>+DZ=Km5c0Y2}`pAQ2-;T+d`Lu~OlGm?d zzP?=gesyrSbM@5Eph8VjP5yo2rW6?SDSZDT_vey}*?GdzccWYJm`v;ZgFDAFLvP2A zYNDE~%JqGB1yq}KDyMuE{i~*&Vs!}6g(R$OW;klnJ%9&0ZtGGI= zx+Ul5C?)-d14y|-g%l~gyFCbecSAXwR~AKgW0>P=V!ff+B>i!Idmqzp?{vC&?{l77 zI%X8)^eetqd|po~8@l2gy2HkDv45>_1yrs7c~cxMDt#OmIuep3B@z%P?Z77dpq@8uneNj!%iAKUWN;ZdfL*d?b& zw9w^w@Le43Z1BVu-9;TF8@k*X%UG}7trH1c*Zw@4md5QcXkrzh6n9y5MIof~F6G1N zm+73n3Zf3W{Nz#!yacPq6!*%=Ctar*&uhmAub2eVIwK#X+~@q*DQ`@h>_1<-ovlPy zmBM|b-$0Qa*?qG2>MFLvZVZ|OzWmVO_-6fcYA^3R`Omcid$xK`?SPq+XJ1(FS?g#~ zyb%%qYC!JU-Pd!pQ!7qMtwFxQZO{AS9j~|3w#Va({fsZ3P>o=n+}z}|CZScF>dd2p ze%J@Kz^!;A#m3g+blw3)BBYTgf<b6xceSavYtNYMRdNkaaO)Q) zwrM<$UOkGIk`e2HD+O5@k$>rMs!r>M1@>|9*JuxTI1r5q)-pJG2-zyd`*N!5a)nWx z&oOh)oJC=581%Vn8QGW!Uh()|rtlBt#A^+AF-L{VZk)Fe=8=mt$kUlTD^%sxv;UAL z{cQ-7XC!RpXk=ne`@}!isYRl2q{D}vZsLg`7@JzgVZfkp*^fJgB&cCr$Bu?)WB+G) zBlCz>LijoEQ7SKKE`7>AiUX=<2}`=-UQA7nWKe|j#ie=Tfi*3u?lfu^p$u%=#V zd@UkBWcaX66N)K7PEA_J-mjrm$KI{UjxHG{vR3r)3AZ&9zNUo4pdmhT(p=YGIj@3byZu`kPZx#?s(Gh+VeZC z>Cn~7Zt*FQIOQW(Zvag%X#Vo#?^hRW2> zCIKHvnDdcWbF@Ll3PONj|EcXeY4}PA34#!uoC#x)U zNkCr7(wB!*qYemEBy_~$r{2lGhx0x_UC~nj@xSRGgL)^b6YrmEaYoEL&H)?UZ>5@M zg56f21f3(xz|oQ`aDCWLt;5Ddp1VFzU@R9yQ1M! z&WgIk{f<)Fr6b1&FFoii+xlA>KT|>i`E$O~6y@MY2RfagZQorD_-b+Diy5b!O}cxu zvHG1)L}?sd+nMM}_+N?3%Exa+rn^q4;^j6tV}ojO-AwFoxXdUl)4ci>c^@nbAIS+JYYJk5hkBv*ah zRi<}^uj%MTW1wN|Wc1R*xG(gOZhjA5oTZOJFAx@rGDl|r`_H~gu94W9oJigEQY5z| zT~{%H-#wOkCa*Am!2kQyz&CTEPZlU>?10P6c z2l7r_GU91GB(gV8;|W+~UePRNPGet3(Zr5sB)B)&;>fia=^!)R+N(HCz<%r5(Z~CF z^=@}(`bE#sHo|pT29il06l8ptc2qiOGU(DK5m6^CuGZCfabKAK?eGYWHP#pz?K?r! zW+Za-WVi54BFAPSG>hIE`V!?(8Fbfo?_P>i|HPmg__l`Ol2`OdFSIzvH|DXa0|uig zy$Ds(_s*|0!F(VA1&wcT&i)8LCN<|Sikk(GP{`~HbFEHdW1mLOz?Z?nYp<9U*rKH_s3UGvXt`-1QfB2dPWDD zqY|0Z0ELvAD?BxuGg-wsYEhGeq)*)% z(JL=V(9x*u$&b>!C+Ca;Nzs$Bt9S28vLpmA%9;3UWm6Mdx&@=lqm{}kr|N3GaD2{J zXDU+uxoZ}@D7bCqKxqRPoKD9>>3>_<^vs1GOW;fT8VP$*myS9e?z=*olO0zt6;SDT zZE8~$?Bv;5ag~UF<6^BC*2AIm?) zyMn9N?Z(4iW!!QP@^*am;>3{vBS-0g^&W9l1;)&mxN3C;eTnXiI4-2;$qpGX3z6et z`{M^Z@~GsiJq)HOW^iiP)jrg#CxWbF_qql}cKaZ^YQi%i&Z5v^A>&N-Gd4e(M$3+; z5!#2H5jOq`jo$WH#uGl`4`;qwzCJ})+Yrb7a^IQvoWXJ~t32{-e7_YUcC~cSdAR4R z8OVKVWW&cYe^q()SNI~MZ~;Z=e}9(SpYHne^}jsXO+)#g2l(exr~X`scufZ|@!K<~ z{=M+uPvZG|X%p~lo_{@;=iiI}`DBv6mm(ppVc#tNe>|t;-^cm)<0}3>5;f?rllYH^ zSN!`Z{~lZaeG~)X|2o9}_W}Ohc>jHXDT$i{{N0BCd+EP>i@%rNmHD;w-<`(4kMPg* y`S*I@0e=ceNdJ*c|GoU56!`n{TgtyJ|AP=Ul+gk2A|c@b-@!n>9#BQR`~Lv-_-7md literal 0 HcmV?d00001 diff --git a/specimen/test/src/org/labkey/test/pages/specimen/ConfigureSpecimenImportPage.java b/specimen/test/src/org/labkey/test/pages/specimen/ConfigureSpecimenImportPage.java new file mode 100644 index 00000000..7114172d --- /dev/null +++ b/specimen/test/src/org/labkey/test/pages/specimen/ConfigureSpecimenImportPage.java @@ -0,0 +1,96 @@ +package org.labkey.test.pages.specimen; + +import org.labkey.test.Locator; +import org.labkey.test.WebDriverWrapper; +import org.labkey.test.WebTestHelper; +import org.labkey.test.pages.LabKeyPage; +import org.labkey.test.pages.study.ManageStudyPage; +import org.labkey.test.util.DataRegionTable; +import org.labkey.test.util.PipelineStatusTable; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; + +import java.util.Optional; + +public class ConfigureSpecimenImportPage extends LabKeyPage +{ + public ConfigureSpecimenImportPage(WebDriver driver) + { + super(driver); + } + + public static ConfigureSpecimenImportPage beginAt(WebDriverWrapper webDriverWrapper) + { + return beginAt(webDriverWrapper, webDriverWrapper.getCurrentContainerPath()); + } + + public static ConfigureSpecimenImportPage beginAt(WebDriverWrapper webDriverWrapper, String containerPath) + { + webDriverWrapper.beginAt(WebTestHelper.buildURL("specimen", containerPath, "configureQueryImport")); + return new ConfigureSpecimenImportPage(webDriverWrapper.getDriver()); + } + + @Override + protected void waitForPage() + { + WebDriverWrapper.waitFor(()-> elementCache().schemaSelect.isEnabled(), + "The page did not become ready in time", WAIT_FOR_JAVASCRIPT); + } + + public ConfigureSpecimenImportPage configureQueryBasedImport(boolean enableReload, String schema, String query, String view) + { + setCheckbox(elementCache().reloadBoxElement, enableReload); + setFormElement(elementCache().schemaSelect, schema); + setFormElement(elementCache().querySelect, query); + setFormElement(elementCache().viewSelect, view); + return this; + } + + public ManageStudyPage clickSave() + { + clickAndWait(elementCache().saveButton); + return new ManageStudyPage(getDriver()); + } + + public ManageStudyPage clickCancel() + { + clickAndWait(elementCache().cancelButton); + return new ManageStudyPage(getDriver()); + } + + public boolean isReloadNowButtonPresent() + { + return elementCache().reloadNowButton().isPresent(); + } + + public DataRegionTable clickReloadNow() + { + doAndWaitForPageToLoad(()-> elementCache().reloadNowButton().get().click()); + return PipelineStatusTable.finder(getDriver()).waitFor(); + } + + @Override + protected ElementCache newElementCache() + { + return new ElementCache(); + } + + protected class ElementCache extends LabKeyPage.ElementCache + { + WebElement cancelButton = Locator.linkWithSpan("Cancel").findWhenNeeded(getDriver()); + WebElement saveButton = Locator.linkWithSpan("Save").findWhenNeeded(getDriver()); + Optional reloadNowButton() + { + return Locator.linkWithSpan("Reload Now").findOptionalElement(getDriver()); + } + + WebElement formContainer = Locator.tagWithClass("div", "QBSpecimenImportFormFields") + .findWhenNeeded(getDriver()).withTimeout(WAIT_FOR_JAVASCRIPT); + + WebElement reloadBoxElement = Locator.checkbox().withAttribute("name", "enabled") + .findWhenNeeded(formContainer); + WebElement schemaSelect = Locator.id("schemaName").findWhenNeeded(formContainer); + WebElement querySelect = Locator.id("queryName").findWhenNeeded(formContainer); + WebElement viewSelect = Locator.id("viewName").findWhenNeeded(formContainer); + } +} diff --git a/specimen/test/src/org/labkey/test/tests/specimen/QueryBasedSpecimenImportTest.java b/specimen/test/src/org/labkey/test/tests/specimen/QueryBasedSpecimenImportTest.java new file mode 100644 index 00000000..8e6df164 --- /dev/null +++ b/specimen/test/src/org/labkey/test/tests/specimen/QueryBasedSpecimenImportTest.java @@ -0,0 +1,114 @@ +package org.labkey.test.tests.specimen; + +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.labkey.test.BaseWebDriverTest; +import org.labkey.test.TestFileUtils; +import org.labkey.test.categories.Git; +import org.labkey.test.categories.Specimen; +import org.labkey.test.pages.specimen.ConfigureSpecimenImportPage; + +import java.io.File; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; + +import static org.hamcrest.CoreMatchers.hasItems; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; + +@Category({Git.class, Specimen.class}) +public class QueryBasedSpecimenImportTest extends BaseWebDriverTest +{ + String TEST_SCHEMA = "lists"; + String TEST_QUERY = "test_specimen_data"; + String TEST_VIEW = "Default"; + File SPECIMENS_FILE = TestFileUtils.getSampleData("import/100_specimens.xlsx"); + + @BeforeClass + public static void setupProject() + { + QueryBasedSpecimenImportTest init = getCurrentTest(); + init.doSetup(); + } + + private void doSetup() + { + _containerHelper.createProject(getProjectName(), "Study"); + _studyHelper.startCreateStudy() + .createStudy(); + goToFolderManagement() + .goToFolderTypeTab() + .enableModule("Specimen") + .save(); + + // create a list of specimens to import from + _listHelper.createListFromFile(getProjectName(), TEST_QUERY, SPECIMENS_FILE); + } + + @Before + public void preTest() + { + goToProjectHome(); + } + + @Test + public void testQueryBasedSpecimenImport() + { + // the test configures one query-based importer + goToManageStudy() + .clickConfigureSpecimenImport(); + new ConfigureSpecimenImportPage(getDriver()) + .configureQueryBasedImport(true, TEST_SCHEMA, TEST_QUERY, TEST_VIEW) + .clickSave(); + + // navigate directly to configure query-based import, user 'reload now' button to load the specimens from the query + ConfigureSpecimenImportPage.beginAt(this, getProjectName()) + .clickReloadNow(); + waitForPipelineJobsToComplete(1, false); + + // navigate to Specimens tab, verify + clickTab("Specimen Data", true); + + // iterate over the list, ensure that for each there is a matching specimen in specimenDetails + List> sourceSpecimenData =executeSelectRowCommand(TEST_SCHEMA, TEST_QUERY).getRows(); + List> specimenDetailsList = executeSelectRowCommand("study", "SpecimenDetail").getRows(); + List> specimenPrimaryTypes = executeSelectRowCommand("study", "SpecimenPrimaryType").getRows(); + + List primaryTypes = specimenPrimaryTypes.stream().map(a-> a.get("PrimaryType").toString()).collect(Collectors.toList()); + assertThat("expect 4 kinds of primary vial types", + primaryTypes, hasItems("PBMC-Na Hep", "Serum-Clot", "urine super", "whole blood")); + + for (Map specimenRow : sourceSpecimenData) + { + String globalUniqueSpecimenId = specimenRow.get("global_unique_specimen_id").toString(); + + Optional> matchingSpecimenDetail = specimenDetailsList.stream() + .filter(a-> a.get("GlobalUniqueId").equals(globalUniqueSpecimenId)).findFirst(); + assertTrue("Expect to find matching SpecimenDetail for [" +globalUniqueSpecimenId+ "]", + matchingSpecimenDetail.isPresent()); + } + } + + @Override + protected BrowserType bestBrowser() + { + return BrowserType.CHROME; + } + + @Override + protected String getProjectName() + { + return "SpecimenImportTest Project"; + } + + @Override + public List getAssociatedModules() + { + return Arrays.asList("Specimen"); + } +} From 04db319b70026cd3362c3a6904aebb4223de1410 Mon Sep 17 00:00:00 2001 From: Nick Kerr Date: Fri, 17 Apr 2026 07:33:05 -0700 Subject: [PATCH 474/494] Triggers: support managing columns for data iteration Original PR: https://github.com/LabKey/platform/pull/7541 --- .../specimen/query/SpecimenUpdateService.java | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/specimen/src/org/labkey/specimen/query/SpecimenUpdateService.java b/specimen/src/org/labkey/specimen/query/SpecimenUpdateService.java index e9d88406..93681880 100644 --- a/specimen/src/org/labkey/specimen/query/SpecimenUpdateService.java +++ b/specimen/src/org/labkey/specimen/query/SpecimenUpdateService.java @@ -70,7 +70,6 @@ public SpecimenUpdateService(TableInfo queryTable) super(queryTable); } - @Override public int importRows(User user, Container container, DataIteratorBuilder rows, BatchValidationException errors, Map configParameters, @Nullable Map extraScriptContext) { @@ -94,7 +93,7 @@ public List> deleteRows(User user, Container container, List BatchValidationException errors = new BatchValidationException(); errors.setExtraContext(extraScriptContext); - getQueryTable().fireBatchTrigger(container, user, TableInfo.TriggerType.DELETE, true, errors, extraScriptContext); + getQueryTable().fireBatchTrigger(container, user, TableInfo.TriggerType.DELETE, null, true, errors, extraScriptContext); Set rowIds = new HashSet<>(keys.size()); for (Map key : keys) @@ -139,7 +138,7 @@ public List> deleteRows(User user, Container container, List throw new IllegalStateException(e.getMessage()); } - getQueryTable().fireBatchTrigger(container, user, TableInfo.TriggerType.DELETE, false, errors, extraScriptContext); + getQueryTable().fireBatchTrigger(container, user, TableInfo.TriggerType.DELETE, null, false, errors, extraScriptContext); addAuditEvent(user, container, QueryService.AuditAction.DELETE, configParameters, null, null, null); @@ -203,7 +202,7 @@ public List> insertRows(User user, Container container, List try { if (hasTableScript) - getQueryTable().fireBatchTrigger(container, user, TableInfo.TriggerType.INSERT, true, errors, extraScriptContext); + getQueryTable().fireBatchTrigger(container, user, TableInfo.TriggerType.INSERT, null, true, errors, extraScriptContext); } catch (BatchValidationException e) { @@ -263,7 +262,7 @@ public List> insertRows(User user, Container container, List try { if (hasTableScript) - getQueryTable().fireBatchTrigger(container, user, TableInfo.TriggerType.INSERT, false, errors, extraScriptContext); + getQueryTable().fireBatchTrigger(container, user, TableInfo.TriggerType.INSERT, null, false, errors, extraScriptContext); } catch (BatchValidationException e) { @@ -332,7 +331,7 @@ public List> updateRows(User user, Container container, List throw new IllegalArgumentException("rows and oldKeys are required to be the same length, but were " + rows.size() + " and " + oldKeys + " in length, respectively"); errors.setExtraContext(extraScriptContext); - getQueryTable().fireBatchTrigger(container, user, TableInfo.TriggerType.UPDATE, true, errors, extraScriptContext); + getQueryTable().fireBatchTrigger(container, user, TableInfo.TriggerType.UPDATE, null, true, errors, extraScriptContext); Set rowIds = new HashSet<>(rows.size()); Map> uniqueRows = new LongHashMap<>(rows.size()); @@ -406,7 +405,7 @@ public List> updateRows(User user, Container container, List throw new IllegalStateException(e.getMessage()); } - getQueryTable().fireBatchTrigger(container, user, TableInfo.TriggerType.UPDATE, false, errors, extraScriptContext); + getQueryTable().fireBatchTrigger(container, user, TableInfo.TriggerType.UPDATE, null, false, errors, extraScriptContext); addAuditEvent(user, container, QueryService.AuditAction.UPDATE, configParameters, rows, null, null); From f86bc45638a683c7017d032ee1270723e64a3f43 Mon Sep 17 00:00:00 2001 From: Josh Eckels Date: Tue, 12 May 2026 07:54:26 -0700 Subject: [PATCH 475/494] Auto-refactor assorted Java lint Original PR: https://github.com/LabKey/testAutomation/pull/2989 #### Rationale Warnings can cover up important code quality issues. #### Related Pull Requests - https://github.com/LabKey/platform/pull/7660 #### Changes - SequencedCollections: `get(0)` -> `getFirst()`, etc - Add missing `@NotNull`/`@Nullable` - Simplify test assertions - Map operation simplification - Remove redundant throws clause - Switch to parameterized log message - C-style array -> Java-style array declaration - Delete overridden methods identical to parent - Switch statement -> enhanced switch statement - Remove redundant imports --- .../test/tests/CustomizeEmailTemplateTest.java | 6 ------ src/org/labkey/test/tests/SpecimenTest.java | 14 ++++---------- 2 files changed, 4 insertions(+), 16 deletions(-) diff --git a/src/org/labkey/test/tests/CustomizeEmailTemplateTest.java b/src/org/labkey/test/tests/CustomizeEmailTemplateTest.java index 21e9d9fb..3120cf2e 100644 --- a/src/org/labkey/test/tests/CustomizeEmailTemplateTest.java +++ b/src/org/labkey/test/tests/CustomizeEmailTemplateTest.java @@ -187,10 +187,4 @@ private String buildTemplateBody() templateBuilder.append("\n
"); return templateBuilder.toString(); } - - @Override - public List getAssociatedModules() - { - return Arrays.asList("study"); - } } diff --git a/src/org/labkey/test/tests/SpecimenTest.java b/src/org/labkey/test/tests/SpecimenTest.java index 6dea72c0..fe574f35 100644 --- a/src/org/labkey/test/tests/SpecimenTest.java +++ b/src/org/labkey/test/tests/SpecimenTest.java @@ -68,12 +68,6 @@ public class SpecimenTest extends SpecimenBaseTest private final PortalHelper _portalHelper = new PortalHelper(this); private final String[] SPECIMEN_IDS = {"AAA07XK5-01", "AAA07XK5-02"}; - @Override - public List getAssociatedModules() - { - return Arrays.asList("study"); - } - @Override protected String getProjectName() { @@ -310,7 +304,7 @@ private void verifyActorDetails() int allCheckBoxes = Locator.xpath("//input[@type='checkbox' and @name='notificationIdPairs']").findElements(getDriver()).size(); int checkedCheckBoxes = Locator.xpath("//input[@type='checkbox' and @name='notificationIdPairs' and @checked]").findElements(getDriver()).size(); int disabledCheckBoxes = Locator.xpath("//input[@type='checkbox' and @name='notificationIdPairs' and @disabled]").findElements(getDriver()).size(); - assertTrue("Actor Notification: All actors should be notified if addresses configured.", allCheckBoxes == checkedCheckBoxes + disabledCheckBoxes); + assertEquals("Actor Notification: All actors should be notified if addresses configured.", allCheckBoxes, checkedCheckBoxes + disabledCheckBoxes); clickButton("Cancel"); } @@ -553,10 +547,10 @@ private void verifyNotificationEmails() for(WebElement message : messages){message.click();} List emailMessages1 = mailTable.getMessagesByHeaderAndText("To", USER1); List emailMessages2 = mailTable.getMessagesByHeaderAndText("To", USER2); - assertTrue(!emailMessages1.get(0).getBody().contains(_specimen_KCMC)); - assertTrue(emailMessages1.get(0).getBody().contains(_specimen_McMichael)); + assertFalse(emailMessages1.getFirst().getBody().contains(_specimen_KCMC)); + assertTrue(emailMessages1.getFirst().getBody().contains(_specimen_McMichael)); assertNotNull("No message found", emailMessages1); - String messageBody = emailMessages2.get(0).getBody().replaceFirst("-*=_Part_\\d{3}_\\d*.\\d*\\n",""); + String messageBody = emailMessages2.getFirst().getBody().replaceFirst("-*=_Part_\\d{3}_\\d*.\\d*\\n",""); messageBody = messageBody.replaceAll("Content-Type: text\\/html; charset=UTF-8\n",""); messageBody = messageBody.replaceAll("Content-Transfer-Encoding: 7bit\n", ""); assertTrue("Notification was not as expected.\nExpected:\n" + notification + "\n\nActual:\n" + messageBody, messageBody.contains(notification)); From 38fc59c7167f5322c06665ca2806b33411beeb0f Mon Sep 17 00:00:00 2001 From: Josh Eckels Date: Tue, 12 May 2026 07:54:30 -0700 Subject: [PATCH 476/494] Auto-refactor assorted Java lint MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Original PR: https://github.com/LabKey/platform/pull/7660 #### Rationale Warnings can cover up important code quality issues. #### Changes - SequencedCollections: `get(0)` -> `getFirst()`, etc - Add missing `@NotNull`/`@Nullable` - Simplify test assertions - Map operation simplification - Remove redundant throws clause - Switch to parameterized log message - C-style array -> Java-style array declaration - Delete overridden methods identical to parent - Switch statement -> enhanced switch statement - Remove redundant imports #### Tasks 📍 - [x] Claude Code Review - ~Manual Testing~ - ~Test Automation~ --- .../labkey/specimen/RequestedSpecimens.java | 2 +- .../org/labkey/specimen/SpecimenManager.java | 14 ++++++------ .../org/labkey/specimen/SpecimenModule.java | 3 +-- .../specimen/SpecimenRequestManager.java | 10 ++++----- .../actions/ReportConfigurationBean.java | 8 +------ .../specimen/actions/SpecimenController.java | 2 +- .../actions/UpdateSpecimenCommentsBean.java | 2 +- .../importer/AbstractSpecimenTask.java | 4 ++-- .../importer/RequestabilityManager.java | 4 ++-- .../specimen/importer/SpecimenImporter.java | 14 ++++++------ .../importer/SpecimenSettingsImporter.java | 13 +++++------ .../pipeline/FileAnalysisSpecimenTask.java | 2 +- .../specimen/pipeline/SpecimenJobSupport.java | 3 +-- .../specimen/pipeline/SpecimenReloadJob.java | 1 - .../pipeline/SpecimenReloadJobSupport.java | 2 -- .../specimen/pipeline/SpecimenReloadTask.java | 2 -- .../query/BaseSpecimenPivotTable.java | 6 +---- .../specimen/query/SpecimenQueryView.java | 22 ++++++------------- .../specimen/query/SpecimenUpdateService.java | 6 ++--- .../specimen/report/SpecimenVisitReport.java | 3 +-- .../RequestParticipantReportFactory.java | 1 - .../TypeParticipantReportFactory.java | 1 - .../view/SpecimenReportWebPartFactory.java | 2 +- .../writer/AbstractSpecimenWriter.java | 6 ----- .../specimen/writer/SpecimenWriter.java | 3 +-- 25 files changed, 50 insertions(+), 86 deletions(-) diff --git a/specimen/src/org/labkey/specimen/RequestedSpecimens.java b/specimen/src/org/labkey/specimen/RequestedSpecimens.java index fb163329..3662eb4c 100644 --- a/specimen/src/org/labkey/specimen/RequestedSpecimens.java +++ b/specimen/src/org/labkey/specimen/RequestedSpecimens.java @@ -43,7 +43,7 @@ public List getProvidingLocations() _providingLocations = Collections.emptyList(); else { - Container container = _vials.get(0).getContainer(); + Container container = _vials.getFirst().getContainer(); _providingLocations = new ArrayList<>(_providingLocationIds.size()); for (Integer locationId : _providingLocationIds) diff --git a/specimen/src/org/labkey/specimen/SpecimenManager.java b/specimen/src/org/labkey/specimen/SpecimenManager.java index 8b1cef9d..0f53cbe8 100644 --- a/specimen/src/org/labkey/specimen/SpecimenManager.java +++ b/specimen/src/org/labkey/specimen/SpecimenManager.java @@ -193,7 +193,7 @@ public Map getSpecimenComments(List vials) if (vials == null || vials.isEmpty()) return Collections.emptyMap(); - Container container = vials.get(0).getContainer(); + Container container = vials.getFirst().getContainer(); final Map result = new HashMap<>(); int offset = 0; @@ -392,7 +392,7 @@ public AdditiveType getAdditiveType(Container c, int rowId) { List additiveTypes = getAdditiveTypes(c, new SimpleFilter(FieldKey.fromParts("RowId"), rowId)); if (!additiveTypes.isEmpty()) - return additiveTypes.get(0); + return additiveTypes.getFirst(); return null; } @@ -408,7 +408,7 @@ public DerivativeType getDerivativeType(Container c, int rowId) { List derivativeTypes = getDerivativeTypes(c, new SimpleFilter(FieldKey.fromParts("RowId"), rowId)); if (!derivativeTypes.isEmpty()) - return derivativeTypes.get(0); + return derivativeTypes.getFirst(); return null; } @@ -444,7 +444,7 @@ public Vial getVial(Container container, User user, String globalUniqueId) throw new IllegalStateException("Expected at least one vial to exactly match the specified global unique ID: " + globalUniqueId); } else - return matches.get(0); + return matches.getFirst(); } /** Looks for any specimens that have the given id as a globalUniqueId */ @@ -452,7 +452,7 @@ public PrimaryType getPrimaryType(Container c, int rowId) { List primaryTypes = getPrimaryTypes(c, new SimpleFilter(FieldKey.fromParts("RowId"), rowId), null); if (!primaryTypes.isEmpty()) - return primaryTypes.get(0); + return primaryTypes.getFirst(); return null; } @@ -529,7 +529,7 @@ public Vial getVial(Container container, User user, long rowId) List vials = getVials(container, user, filter); if (vials.isEmpty()) return null; - return vials.get(0); + return vials.getFirst(); } public void deleteSpecimen(@NotNull Vial vial, boolean clearCaches) @@ -564,7 +564,7 @@ public long getMaxExternalId(Container container) TableInfo tableInfo = SpecimenSchema.get().getTableInfoSpecimenEvent(container); SQLFragment sql = new SQLFragment("SELECT MAX(ExternalId) FROM "); sql.append(tableInfo); - return new SqlSelector(tableInfo.getSchema(), sql).getArrayList(Long.class).get(0); + return new SqlSelector(tableInfo.getSchema(), sql).getArrayList(Long.class).getFirst(); } public String getFirstProcessedByInitials(List dateOrderedEvents) diff --git a/specimen/src/org/labkey/specimen/SpecimenModule.java b/specimen/src/org/labkey/specimen/SpecimenModule.java index 833af41d..798e90c4 100644 --- a/specimen/src/org/labkey/specimen/SpecimenModule.java +++ b/specimen/src/org/labkey/specimen/SpecimenModule.java @@ -41,7 +41,6 @@ import org.labkey.api.query.QueryUpdateService; import org.labkey.api.query.QueryView; import org.labkey.api.query.UserSchema; -import org.labkey.api.query.ValidationException; import org.labkey.api.security.User; import org.labkey.api.security.roles.RoleManager; import org.labkey.api.specimen.SpecimenMigrationService; @@ -174,7 +173,7 @@ public ActionURL getSpecimensURL(Container c) } @Override - public void importSpecimenArchive(@Nullable FileLike inputFile, PipelineJob job, SimpleStudyImportContext ctx, boolean merge, boolean syncParticipantVisit) throws PipelineJobException, ValidationException + public void importSpecimenArchive(@Nullable FileLike inputFile, PipelineJob job, SimpleStudyImportContext ctx, boolean merge, boolean syncParticipantVisit) throws PipelineJobException { AbstractSpecimenTask.doImport(inputFile, job, ctx, merge, syncParticipantVisit); } diff --git a/specimen/src/org/labkey/specimen/SpecimenRequestManager.java b/specimen/src/org/labkey/specimen/SpecimenRequestManager.java index 9a62c131..2089c35b 100644 --- a/specimen/src/org/labkey/specimen/SpecimenRequestManager.java +++ b/specimen/src/org/labkey/specimen/SpecimenRequestManager.java @@ -106,7 +106,7 @@ public List getRequestStatuses(Container c, User user) List statuses = new ArrayList<>(_requestStatusHelper.getCollection(c)); // if the 'not-yet-submitted' status doesn't exist, create it here, with sort order -1, // so it's always first. - if (statuses.isEmpty() || statuses.get(0).getSortOrder() != -1) + if (statuses.isEmpty() || statuses.getFirst().getSortOrder() != -1) { SpecimenRequestStatus notYetSubmittedStatus = new SpecimenRequestStatus(); notYetSubmittedStatus.setContainer(c); @@ -127,9 +127,9 @@ public List getRequestStatuses(Container c, User user) public SpecimenRequestStatus getRequestShoppingCartStatus(Container c, User user) { List statuses = getRequestStatuses(c, user); - if (statuses.get(0).getSortOrder() != -1) + if (statuses.getFirst().getSortOrder() != -1) throw new IllegalStateException("Shopping cart status should be created automatically."); - return statuses.get(0); + return statuses.getFirst(); } public SpecimenRequestStatus getInitialRequestStatus(Container c, User user, boolean nonCart) @@ -651,7 +651,7 @@ private void updateRequestabilityAndCounts(List vials, User user) throws R private Container getContainer(List vials) { - Container container = vials.get(0).getContainer(); + Container container = vials.getFirst().getContainer(); if (AppProps.getInstance().isDevMode()) { for (int i = 1; i < vials.size(); i++) @@ -1253,7 +1253,7 @@ else if (preferredLocations.size() > 1) { Vial selectedVial = null; if (preferredLocation == null) - selectedVial = vials.get(0); + selectedVial = vials.getFirst(); else { for (Iterator it = vials.iterator(); it.hasNext() && selectedVial == null;) diff --git a/specimen/src/org/labkey/specimen/actions/ReportConfigurationBean.java b/specimen/src/org/labkey/specimen/actions/ReportConfigurationBean.java index 9cdd98a0..54c6edc4 100644 --- a/specimen/src/org/labkey/specimen/actions/ReportConfigurationBean.java +++ b/specimen/src/org/labkey/specimen/actions/ReportConfigurationBean.java @@ -82,13 +82,7 @@ private void registerReportFactory(String category, SpecimenVisitReportParameter { // we have to explicitly set the view context for these reports, since the factories aren't being newed-up by Spring in the usual way: factory.setViewContext(_viewContext); - List factories = _reportFactories.get(category); - - if (factories == null) - { - factories = new ArrayList<>(); - _reportFactories.put(category, factories); - } + List factories = _reportFactories.computeIfAbsent(category, _ -> new ArrayList<>()); factories.add(factory); } diff --git a/specimen/src/org/labkey/specimen/actions/SpecimenController.java b/specimen/src/org/labkey/specimen/actions/SpecimenController.java index e2fd64a0..5fb8abaa 100644 --- a/specimen/src/org/labkey/specimen/actions/SpecimenController.java +++ b/specimen/src/org/labkey/specimen/actions/SpecimenController.java @@ -5680,7 +5680,7 @@ private String getExistingComment(QueryView queryView) } catch (Exception e) { - LOG.error("Error encountered trying to get " + StudyService.get().getSubjectNounSingular(getContainer()) + " comments", e); + LOG.error("Error encountered trying to get {} comments", StudyService.get().getSubjectNounSingular(getContainer()), e); } } return null; diff --git a/specimen/src/org/labkey/specimen/actions/UpdateSpecimenCommentsBean.java b/specimen/src/org/labkey/specimen/actions/UpdateSpecimenCommentsBean.java index 46ba8d4d..c4dce316 100644 --- a/specimen/src/org/labkey/specimen/actions/UpdateSpecimenCommentsBean.java +++ b/specimen/src/org/labkey/specimen/actions/UpdateSpecimenCommentsBean.java @@ -35,7 +35,7 @@ public UpdateSpecimenCommentsBean(ViewContext context, List vials, String Map currentComments = SpecimenManager.get().getSpecimenComments(vials); boolean mixedComments = false; boolean mixedFlagState = false; - SpecimenComment prevComment = currentComments.get(vials.get(0)); + SpecimenComment prevComment = currentComments.get(vials.getFirst()); for (int i = 1; i < vials.size() && (!mixedFlagState || !mixedComments); i++) { diff --git a/specimen/src/org/labkey/specimen/importer/AbstractSpecimenTask.java b/specimen/src/org/labkey/specimen/importer/AbstractSpecimenTask.java index 951b261b..e6192567 100644 --- a/specimen/src/org/labkey/specimen/importer/AbstractSpecimenTask.java +++ b/specimen/src/org/labkey/specimen/importer/AbstractSpecimenTask.java @@ -223,12 +223,12 @@ public VirtualFile getSpecimenDir(PipelineJob job, SimpleStudyImportContext ctx, if (job != null) job.setStatus("UNZIPPING SPECIMEN ARCHIVE"); - ctx.getLogger().info("Unzipping specimen archive " + specimenArchive); + ctx.getLogger().info("Unzipping specimen archive {}", specimenArchive); String tempDirName = DateUtil.formatDateTime(new Date(), "yyMMddHHmmssSSS"); _unzipDir = specimenArchive.getParent().resolveChild(tempDirName); ZipUtil.unzipToDirectory(specimenArchive, _unzipDir, ctx.getLogger()); - ctx.getLogger().info("Archive unzipped to " + _unzipDir); + ctx.getLogger().info("Archive unzipped to {}", _unzipDir); return new FileSystemFile(_unzipDir); } } diff --git a/specimen/src/org/labkey/specimen/importer/RequestabilityManager.java b/specimen/src/org/labkey/specimen/importer/RequestabilityManager.java index cc6ccdc0..f9afa4a6 100644 --- a/specimen/src/org/labkey/specimen/importer/RequestabilityManager.java +++ b/specimen/src/org/labkey/specimen/importer/RequestabilityManager.java @@ -824,10 +824,10 @@ private void updateRequestability(Container container, User user, boolean resetT { String action = rule.getMarkType().getLabel().toLowerCase(); if (logger != null) - logger.info("\tMarking vials " + action + " based on " + rule.getName()); + logger.info("\tMarking vials {} based on {}", action, rule.getName()); int updatedCount = rule.updateRequestability(user, vials); if (logger != null) - logger.info("\t" + updatedCount + " vials marked " + action + "."); + logger.info("\t{} vials marked {}.", updatedCount, action); } if (logger != null) diff --git a/specimen/src/org/labkey/specimen/importer/SpecimenImporter.java b/specimen/src/org/labkey/specimen/importer/SpecimenImporter.java index 86e5d046..26f620a5 100644 --- a/specimen/src/org/labkey/specimen/importer/SpecimenImporter.java +++ b/specimen/src/org/labkey/specimen/importer/SpecimenImporter.java @@ -2561,15 +2561,15 @@ protected void checkForConflictingSpecimens(DbSchema schema, String tempTable, L { throw new IllegalStateException("Expected one and only one count of rows."); } - else if (0 != counts.get(0) && _generateGlobalUniqueIds > 0) + else if (0 != counts.getFirst() && _generateGlobalUniqueIds > 0) { // We were trying to generate globalUniqueIds throw new OptimisticConflictException("Attempt to generate global unique ids failed.", null, 0); } - else if (0 != counts.get(0)) + else if (0 != counts.getFirst()) { throw new IllegalStateException("With an editable specimen repository, importing may not reference any existing specimen. " + - counts.get(0) + " imported specimen events refer to existing specimens.") ; + counts.getFirst() + " imported specimen events refer to existing specimens.") ; } info("No conflicting specimens found"); } @@ -2724,7 +2724,7 @@ private TempTablesHolder createTempTable() sql.append("\n(\n RowId ").append(dialect.getUniqueIdentType()).append(", "); columns.add(new BaseColumnInfo("RowId", JdbcType.INTEGER, 0, false)); - columns.get(0).setAutoIncrement(true); + columns.getFirst().setAutoIncrement(true); sql.append("LSID ").append(strType).append("(300) NOT NULL, "); columns.add(new BaseColumnInfo("LSID", JdbcType.VARCHAR, 300, false)); @@ -2837,7 +2837,7 @@ public void createTable() columns.add(new BaseColumnInfo("Container", JdbcType.GUID, 0, false)); columns.add(new BaseColumnInfo("id", JdbcType.VARCHAR, 0, false)); columns.add(new BaseColumnInfo("s", JdbcType.VARCHAR, 30, true)); - columns.get(columns.size()-1).setKeyField(true); + columns.getLast().setKeyField(true); columns.add(new BaseColumnInfo("i", JdbcType.INTEGER, 0, true)); columns.add(new BaseColumnInfo("entityid", JdbcType.GUID, 0, false)); _simpleTable = new TempTableInfo(TABLE, (List)(List)columns, Arrays.asList("s")); @@ -2929,7 +2929,7 @@ public Object getValue(Map row) sallyGUID = (String)row1.get("entityid"); // assertNotNull(sallyGUID); - assertEquals(null, row2.get("s")); + assertNull(row2.get("s")); assertEquals(300, row2.get("i")); assertEquals("3", row2.get("id")); nullGUID = (String)row2.get("entityid"); @@ -2971,7 +2971,7 @@ public Object getValue(Map row) assertEquals("2", row1.get("id")); assertEquals(sallyGUID, row1.get("entityid")); - assertEquals(null, row2.get("s")); + assertNull(row2.get("s")); assertEquals(305, row2.get("i")); assertEquals("3", row2.get("id")); assertEquals(nullGUID, row2.get("entityid")); diff --git a/specimen/src/org/labkey/specimen/importer/SpecimenSettingsImporter.java b/specimen/src/org/labkey/specimen/importer/SpecimenSettingsImporter.java index 8cbd2ad4..cc6d224e 100644 --- a/specimen/src/org/labkey/specimen/importer/SpecimenSettingsImporter.java +++ b/specimen/src/org/labkey/specimen/importer/SpecimenSettingsImporter.java @@ -113,7 +113,7 @@ public void process(SimpleStudyImportContext ctx, VirtualFile studyDir, BindExce SpecimensDocument specimensDoc = (SpecimensDocument)settingsDir.getXmlBean(xmlSettings.getSettings()); importSettings(ctx, specimensDoc.getSpecimens()); - ctx.getLogger().info("Done importing " + getDescription()); + ctx.getLogger().info("Done importing {}", getDescription()); } } } @@ -203,7 +203,7 @@ private void importRequestStatuses(SimpleStudyImportContext ctx, SpecimenSetting String newStatusLabel = xmlStatusArray[i].isSetLabel() && xmlStatusArray[i].getLabel() != null && !xmlStatusArray[i].getLabel().isEmpty() ? xmlStatusArray[i].getLabel() : null; if (inUseStatusLabels.contains(newStatusLabel)) { - ctx.getLogger().warn("Skipping request status that matches an existing status label: " + newStatusLabel); + ctx.getLogger().warn("Skipping request status that matches an existing status label: {}", newStatusLabel); } else if (newStatusLabel != null) { @@ -289,8 +289,7 @@ private void importRequestActors(SimpleStudyImportContext ctx, SpecimenSettingsT if (location == null) { - ctx.getLogger().warn("Request actor group not created for \"" + actor.getLabel() - + ", " + newActorGroup.getName() + "\". Could not find matching study location."); + ctx.getLogger().warn("Request actor group not created for \"{}, {}\". Could not find matching study location.", actor.getLabel(), newActorGroup.getName()); continue; } } @@ -354,14 +353,14 @@ private void createDefaultRequirement(DefaultRequirementsType xmlReqs, SimpleStu { SpecimenRequestRequirement requirement = new SpecimenRequestRequirement(); requirement.setContainer(ctx.getContainer()); - requirement.setActorId(matchingActors.get(0).getRowId()); + requirement.setActorId(matchingActors.getFirst().getRowId()); requirement.setDescription(xmlReq.getDescription()); requirement.setRequestId(-1); SpecimenRequestRequirementProvider.get().createDefaultRequirement(ctx.getUser(), requirement, type); } else { - ctx.getLogger().warn("Could not find matching actor with label: " + xmlReq.getActor()); + ctx.getLogger().warn("Could not find matching actor with label: {}", xmlReq.getActor()); } } } @@ -425,7 +424,7 @@ private void importRequestForm(SimpleStudyImportContext ctx, SpecimenSettingsTyp inputs.add(input); } else - ctx.getLogger().info("There is currently a form with the same title: " + form.getTitle() + ", skipping this from import"); + ctx.getLogger().info("There is currently a form with the same title: {}, skipping this from import", form.getTitle()); } inputs.sort(Comparator.comparingInt(SpecimenRequestInput::getDisplayOrder)); SpecimenRequestManager.get().saveNewSpecimenRequestInputs(ctx.getContainer(), inputs.toArray(new SpecimenRequestInput[0])); diff --git a/specimen/src/org/labkey/specimen/pipeline/FileAnalysisSpecimenTask.java b/specimen/src/org/labkey/specimen/pipeline/FileAnalysisSpecimenTask.java index 546b5e0d..680c5b43 100644 --- a/specimen/src/org/labkey/specimen/pipeline/FileAnalysisSpecimenTask.java +++ b/specimen/src/org/labkey/specimen/pipeline/FileAnalysisSpecimenTask.java @@ -41,7 +41,7 @@ protected FileLike getSpecimenFile(PipelineJob job) List paths = support.getInputFiles(); // there should only be a single file associated with this task assert paths.size() == 1; - return paths.get(0); + return paths.getFirst(); } @Override diff --git a/specimen/src/org/labkey/specimen/pipeline/SpecimenJobSupport.java b/specimen/src/org/labkey/specimen/pipeline/SpecimenJobSupport.java index 11043224..198155df 100644 --- a/specimen/src/org/labkey/specimen/pipeline/SpecimenJobSupport.java +++ b/specimen/src/org/labkey/specimen/pipeline/SpecimenJobSupport.java @@ -16,7 +16,6 @@ package org.labkey.specimen.pipeline; -import org.labkey.api.admin.ImportException; import org.labkey.api.study.importer.SimpleStudyImportContext; import org.labkey.vfs.FileLike; @@ -27,7 +26,7 @@ */ public interface SpecimenJobSupport { - FileLike getSpecimenArchivePath() throws ImportException; + FileLike getSpecimenArchivePath(); boolean isMerge(); SimpleStudyImportContext getImportContext(); } diff --git a/specimen/src/org/labkey/specimen/pipeline/SpecimenReloadJob.java b/specimen/src/org/labkey/specimen/pipeline/SpecimenReloadJob.java index 4ddbbb6e..819dc54c 100644 --- a/specimen/src/org/labkey/specimen/pipeline/SpecimenReloadJob.java +++ b/specimen/src/org/labkey/specimen/pipeline/SpecimenReloadJob.java @@ -24,7 +24,6 @@ import org.labkey.api.view.ViewBackgroundInfo; import org.labkey.vfs.FileLike; -import java.io.File; import java.io.Serializable; /** diff --git a/specimen/src/org/labkey/specimen/pipeline/SpecimenReloadJobSupport.java b/specimen/src/org/labkey/specimen/pipeline/SpecimenReloadJobSupport.java index 2ecc7cd0..5732b0b6 100644 --- a/specimen/src/org/labkey/specimen/pipeline/SpecimenReloadJobSupport.java +++ b/specimen/src/org/labkey/specimen/pipeline/SpecimenReloadJobSupport.java @@ -18,8 +18,6 @@ import org.labkey.api.study.SpecimenTransform; import org.labkey.vfs.FileLike; -import java.io.File; - /** * Created by klum on 5/24/2014. */ diff --git a/specimen/src/org/labkey/specimen/pipeline/SpecimenReloadTask.java b/specimen/src/org/labkey/specimen/pipeline/SpecimenReloadTask.java index 6a88f0e8..eade63b4 100644 --- a/specimen/src/org/labkey/specimen/pipeline/SpecimenReloadTask.java +++ b/specimen/src/org/labkey/specimen/pipeline/SpecimenReloadTask.java @@ -28,9 +28,7 @@ import org.labkey.api.util.FileType; import org.labkey.api.util.FileUtil; import org.labkey.vfs.FileLike; -import org.labkey.vfs.FileSystemLike; -import java.io.File; import java.util.Collections; import java.util.List; diff --git a/specimen/src/org/labkey/specimen/query/BaseSpecimenPivotTable.java b/specimen/src/org/labkey/specimen/query/BaseSpecimenPivotTable.java index 03455d23..5d733c1a 100644 --- a/specimen/src/org/labkey/specimen/query/BaseSpecimenPivotTable.java +++ b/specimen/src/org/labkey/specimen/query/BaseSpecimenPivotTable.java @@ -181,11 +181,7 @@ public BaseSpecimenPivotTable(final TableInfo tinfo, final UserSchema schema) { super(tinfo, schema); - LogManager.getLogger(BaseSpecimenPivotTable.class).debug("creating specimen pivot\n" + - "SCHEMA=" + schema.getName() + " " + schema.getClass().getSimpleName()+"@"+System.identityHashCode(schema) + "\n" + - "TABLE=" + tinfo.getName() + " " + this.getClass().getSimpleName() + "@" + System.identityHashCode(this), - new Throwable("stack trace") - ); + LogManager.getLogger(BaseSpecimenPivotTable.class).debug("creating specimen pivot\nSCHEMA={} {}@{}\nTABLE={} {}@{}", schema.getName(), schema.getClass().getSimpleName(), System.identityHashCode(schema), tinfo.getName(), this.getClass().getSimpleName(), System.identityHashCode(this), new Throwable("stack trace")); addWrapColumn(_rootTable.getColumn(StudyService.get().getSubjectColumnName(getContainer()))); addWrapColumn(_rootTable.getColumn(StudyService.get().getSubjectVisitColumnName(getContainer()))); diff --git a/specimen/src/org/labkey/specimen/query/SpecimenQueryView.java b/specimen/src/org/labkey/specimen/query/SpecimenQueryView.java index 1126886f..151d7875 100644 --- a/specimen/src/org/labkey/specimen/query/SpecimenQueryView.java +++ b/specimen/src/org/labkey/specimen/query/SpecimenQueryView.java @@ -21,7 +21,6 @@ import org.labkey.api.collections.ResultSetRowMapFactory; import org.labkey.api.data.AbstractTableInfo; import org.labkey.api.data.Aggregate; -import org.labkey.api.data.ColumnHeaderType; import org.labkey.api.data.ColumnInfo; import org.labkey.api.data.Container; import org.labkey.api.data.ContainerManager; @@ -71,7 +70,6 @@ import org.labkey.specimen.settings.RepositorySettings; import org.labkey.specimen.settings.SettingsManager; -import java.io.IOException; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; @@ -871,19 +869,19 @@ public List getDisplayColumns() if (_viewType.isForExport()) { // Remove rowId column and update column, if present - DisplayColumn column = cols.get(0); + DisplayColumn column = cols.getFirst(); if (column.getName().equalsIgnoreCase("rowid") || column.getName().equalsIgnoreCase("update")) - cols.remove(0); - column = cols.get(0); + cols.removeFirst(); + column = cols.getFirst(); if (column.getName().equalsIgnoreCase("rowid") || column.getName().equalsIgnoreCase("update")) - cols.remove(0); + cols.removeFirst(); } if (_viewType.isVialView()) { if (_showHistoryLinks) { - cols.add(0, new SimpleDisplayColumn("[history]") + cols.addFirst(new SimpleDisplayColumn("[history]") { @Override public String renderURL(RenderContext ctx) @@ -913,7 +911,7 @@ public String renderURL(RenderContext ctx) if (settings.isEnableRequests() && !_viewType.isForExport() && getViewContext().getContainer().hasPermission(getUser(), RequestSpecimensPermission.class)) { // Only add this column if we're using advanced specimen management and not exported to email or attachment - cols.add(0, new SpecimenRequestDisplayColumn(this, getTable(), zeroVialIndicator, oneVialIndicator, + cols.addFirst(new SpecimenRequestDisplayColumn(this, getTable(), zeroVialIndicator, oneVialIndicator, SettingsManager.get().isSpecimenShoppingCartEnabled(getContainer()) && _showRecordSelectors)); } @@ -982,7 +980,7 @@ public void setShowRecordSelectors(boolean showRecordSelectors) _showRecordSelectors = showRecordSelectors; } - public String getSimpleHtmlTable() throws SQLException, IOException + public String getSimpleHtmlTable() throws SQLException { getSettings().setMaxRows(Table.ALL_ROWS); DataView view = createDataView(); @@ -1037,10 +1035,4 @@ public void addManageViewItems(MenuButton button, Map params) button.addMenuItem("Manage Views", url); } - - @Override - protected ColumnHeaderType getColumnHeaderType() - { - return ColumnHeaderType.Caption; - } } diff --git a/specimen/src/org/labkey/specimen/query/SpecimenUpdateService.java b/specimen/src/org/labkey/specimen/query/SpecimenUpdateService.java index 93681880..e42a9afa 100644 --- a/specimen/src/org/labkey/specimen/query/SpecimenUpdateService.java +++ b/specimen/src/org/labkey/specimen/query/SpecimenUpdateService.java @@ -396,7 +396,7 @@ public List> updateRows(User user, Container container, List } catch (ValidationException e) { - e.fillIn(getQueryTable().getPublicSchemaName(), getQueryTable().getName(), newRows.get(0), 0); + e.fillIn(getQueryTable().getPublicSchemaName(), getQueryTable().getName(), newRows.getFirst(), 0); errors.addRowError(e); throw errors; } @@ -516,7 +516,7 @@ private void checkEditability(Container container, Vial vial) throws ValidationE ArrayList counts = new SqlSelector(getQueryTable().getSchema(), sql).getArrayList(Integer.class); if (counts.size() > 1) throw new IllegalStateException("Expected one and only one count of rows."); - else if (!counts.isEmpty() && counts.get(0) != 0) + else if (!counts.isEmpty() && counts.getFirst() != 0) throw new ValidationException("Specimen may not be edited when it's in a non-final request."); } @@ -529,7 +529,7 @@ private void checkDeletability(Container container, Vial vial) throws Validation ArrayList counts = new SqlSelector(getQueryTable().getSchema(), sql).getArrayList(Integer.class); if (counts.size() > 1) throw new ValidationException("Expected one and only one count of rows."); - else if (!counts.isEmpty() && counts.get(0) != 0) + else if (!counts.isEmpty() && counts.getFirst() != 0) throw new ValidationException("Specimen may not be deleted because it has been used in a request."); } diff --git a/specimen/src/org/labkey/specimen/report/SpecimenVisitReport.java b/specimen/src/org/labkey/specimen/report/SpecimenVisitReport.java index d6c08bee..eee91eb4 100644 --- a/specimen/src/org/labkey/specimen/report/SpecimenVisitReport.java +++ b/specimen/src/org/labkey/specimen/report/SpecimenVisitReport.java @@ -38,7 +38,6 @@ import java.util.ArrayList; import java.util.Collection; -import java.util.HashMap; import java.util.List; import java.util.Map; @@ -307,7 +306,7 @@ protected SimpleFilter replaceFilterParameterName(SimpleFilter filter, String ol FieldKey oldFieldKey = FieldKey.fromString(oldKey); for (SimpleFilter.FilterClause clause : filter.getClauses()) { - if (clause.getFieldKeys().size() == 1 && oldFieldKey.equals(clause.getFieldKeys().get(0))) + if (clause.getFieldKeys().size() == 1 && oldFieldKey.equals(clause.getFieldKeys().getFirst())) { if (clause.getParamVals().length > 1) throw new UnsupportedOperationException("Only single filters are supported on column " + newKey); diff --git a/specimen/src/org/labkey/specimen/report/request/RequestParticipantReportFactory.java b/specimen/src/org/labkey/specimen/report/request/RequestParticipantReportFactory.java index a8c6f66e..59a1aa7d 100644 --- a/specimen/src/org/labkey/specimen/report/request/RequestParticipantReportFactory.java +++ b/specimen/src/org/labkey/specimen/report/request/RequestParticipantReportFactory.java @@ -37,7 +37,6 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import java.util.HashMap; import java.util.List; import java.util.Map; diff --git a/specimen/src/org/labkey/specimen/report/specimentype/TypeParticipantReportFactory.java b/specimen/src/org/labkey/specimen/report/specimentype/TypeParticipantReportFactory.java index eef11f44..72f8e31b 100644 --- a/specimen/src/org/labkey/specimen/report/specimentype/TypeParticipantReportFactory.java +++ b/specimen/src/org/labkey/specimen/report/specimentype/TypeParticipantReportFactory.java @@ -38,7 +38,6 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import java.util.HashMap; import java.util.List; import java.util.Map; diff --git a/specimen/src/org/labkey/specimen/view/SpecimenReportWebPartFactory.java b/specimen/src/org/labkey/specimen/view/SpecimenReportWebPartFactory.java index 8c1ae4c6..31521b94 100644 --- a/specimen/src/org/labkey/specimen/view/SpecimenReportWebPartFactory.java +++ b/specimen/src/org/labkey/specimen/view/SpecimenReportWebPartFactory.java @@ -48,7 +48,7 @@ private SpecimenVisitReportParameters getFactory(ViewContext context, Portal.Web Set categories = bean.getCategories(); // First element of first category is the default (should be "TypeSummary") - SpecimenVisitReportParameters factory = bean.getFactories(categories.iterator().next()).get(0); + SpecimenVisitReportParameters factory = bean.getFactories(categories.iterator().next()).getFirst(); assert "TypeSummary".equals(factory.getReportType()); if (null == name) diff --git a/specimen/src/org/labkey/specimen/writer/AbstractSpecimenWriter.java b/specimen/src/org/labkey/specimen/writer/AbstractSpecimenWriter.java index c8cc3c11..189b389b 100644 --- a/specimen/src/org/labkey/specimen/writer/AbstractSpecimenWriter.java +++ b/specimen/src/org/labkey/specimen/writer/AbstractSpecimenWriter.java @@ -27,12 +27,6 @@ abstract class AbstractSpecimenWriter implements SimpleStudyWriter { protected static final String DEFAULT_DIRECTORY = "specimens"; - @Override - public boolean includeWithTemplate() - { - return true; - } - protected StudyDocument.Study.Specimens ensureSpecimensElement(AbstractStudyContext ctx) throws Exception { StudyDocument.Study studyXml = ctx.getXml(); diff --git a/specimen/src/org/labkey/specimen/writer/SpecimenWriter.java b/specimen/src/org/labkey/specimen/writer/SpecimenWriter.java index de1b2b40..966ec35a 100644 --- a/specimen/src/org/labkey/specimen/writer/SpecimenWriter.java +++ b/specimen/src/org/labkey/specimen/writer/SpecimenWriter.java @@ -94,8 +94,7 @@ public void write(Study study, SimpleStudyExportContext ctx, VirtualFile vf) thr ColumnInfo ci = tinfo.getColumn(column.getDbColumnName()); if (null == ci) { - ctx.getLogger().warn("Specimen Column '" + column.getDbColumnName() + "' not found in table '" + - tinfo.getPublicName() + "'"); + ctx.getLogger().warn("Specimen Column '{}' not found in table '{}'", column.getDbColumnName(), tinfo.getPublicName()); continue; } DataColumn dc = new DataColumn(ci); From 207a80acc0da840ed085de3dbcc5fc501e3388d0 Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Wed, 27 May 2026 11:27:52 -0700 Subject: [PATCH 477/494] Update copyrights and licenses Original PR: https://github.com/LabKey/testAutomation/pull/3020 #### Rationale Updates for 2026: https://github.com/LabKey/internal-issues/issues/1184 --- src/org/labkey/test/tests/AliquotTest.java | 2 +- src/org/labkey/test/tests/CustomizeEmailTemplateTest.java | 2 +- src/org/labkey/test/tests/SpecimenBaseTest.java | 2 +- src/org/labkey/test/tests/SpecimenCustomizeTest.java | 2 +- src/org/labkey/test/tests/SpecimenExportTest.java | 2 +- src/org/labkey/test/tests/SpecimenGridExportTest.java | 2 +- src/org/labkey/test/tests/SpecimenImportTest.java | 2 +- src/org/labkey/test/tests/SpecimenMultipleImportTest.java | 2 +- src/org/labkey/test/tests/SpecimenTest.java | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/org/labkey/test/tests/AliquotTest.java b/src/org/labkey/test/tests/AliquotTest.java index 3a95f168..2730f46f 100644 --- a/src/org/labkey/test/tests/AliquotTest.java +++ b/src/org/labkey/test/tests/AliquotTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2019 LabKey Corporation + * Copyright (c) 2013-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/org/labkey/test/tests/CustomizeEmailTemplateTest.java b/src/org/labkey/test/tests/CustomizeEmailTemplateTest.java index 3120cf2e..79dd8665 100644 --- a/src/org/labkey/test/tests/CustomizeEmailTemplateTest.java +++ b/src/org/labkey/test/tests/CustomizeEmailTemplateTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2019 LabKey Corporation + * Copyright (c) 2013-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/org/labkey/test/tests/SpecimenBaseTest.java b/src/org/labkey/test/tests/SpecimenBaseTest.java index 25c52e34..229694fc 100644 --- a/src/org/labkey/test/tests/SpecimenBaseTest.java +++ b/src/org/labkey/test/tests/SpecimenBaseTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2017 LabKey Corporation + * Copyright (c) 2013-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/org/labkey/test/tests/SpecimenCustomizeTest.java b/src/org/labkey/test/tests/SpecimenCustomizeTest.java index d01a2a27..3feed6ae 100644 --- a/src/org/labkey/test/tests/SpecimenCustomizeTest.java +++ b/src/org/labkey/test/tests/SpecimenCustomizeTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 LabKey Corporation + * Copyright (c) 2014-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/org/labkey/test/tests/SpecimenExportTest.java b/src/org/labkey/test/tests/SpecimenExportTest.java index b5d3633a..077846be 100644 --- a/src/org/labkey/test/tests/SpecimenExportTest.java +++ b/src/org/labkey/test/tests/SpecimenExportTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2019 LabKey Corporation + * Copyright (c) 2013-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/org/labkey/test/tests/SpecimenGridExportTest.java b/src/org/labkey/test/tests/SpecimenGridExportTest.java index 89d7aab9..1a5cd8fa 100644 --- a/src/org/labkey/test/tests/SpecimenGridExportTest.java +++ b/src/org/labkey/test/tests/SpecimenGridExportTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 LabKey Corporation + * Copyright (c) 2014-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/org/labkey/test/tests/SpecimenImportTest.java b/src/org/labkey/test/tests/SpecimenImportTest.java index 28f5d158..f8fd54a6 100644 --- a/src/org/labkey/test/tests/SpecimenImportTest.java +++ b/src/org/labkey/test/tests/SpecimenImportTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2019 LabKey Corporation + * Copyright (c) 2013-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/org/labkey/test/tests/SpecimenMultipleImportTest.java b/src/org/labkey/test/tests/SpecimenMultipleImportTest.java index 1fc06941..b42742c5 100644 --- a/src/org/labkey/test/tests/SpecimenMultipleImportTest.java +++ b/src/org/labkey/test/tests/SpecimenMultipleImportTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2018 LabKey Corporation + * Copyright (c) 2013-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/org/labkey/test/tests/SpecimenTest.java b/src/org/labkey/test/tests/SpecimenTest.java index fe574f35..9051114f 100644 --- a/src/org/labkey/test/tests/SpecimenTest.java +++ b/src/org/labkey/test/tests/SpecimenTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011-2019 LabKey Corporation + * Copyright (c) 2008-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. From 8814729a4efa42be9d0c926a5a0fcc491b9f1d38 Mon Sep 17 00:00:00 2001 From: Josh Eckels Date: Mon, 1 Jun 2026 16:41:57 -0700 Subject: [PATCH 478/494] GitHub Issue 1188: Stop JDBC caching by default in QueryService Original PR: https://github.com/LabKey/platform/pull/7708 #### Rationale We can improve our memory usage by avoiding unwanted JDBC caching. We can also simplify `QueryService` usage by consolidating to use `SelectBuilder`. #### Changes - Disable Postgres JDBC caching by default for `QueryService` method like `select()` - Switch to `getSelectBuilder()` and `SelectBuilder` patterns - Eliminate unneeded parameters, using defaults when possible --------- Co-authored-by: Claude Sonnet 4.6 --- specimen/src/org/labkey/specimen/SpecimenRequestManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specimen/src/org/labkey/specimen/SpecimenRequestManager.java b/specimen/src/org/labkey/specimen/SpecimenRequestManager.java index 2089c35b..a26d066e 100644 --- a/specimen/src/org/labkey/specimen/SpecimenRequestManager.java +++ b/specimen/src/org/labkey/specimen/SpecimenRequestManager.java @@ -949,7 +949,7 @@ public Map> load(@NotNull Container c, @Nullable Obj TableInfo tableInfo = schema.getTable(SpecimenTablesProvider.SPECIMEN_WRAP_TABLE_NAME); Map columnMap = queryService.getColumns(tableInfo, fieldKeys); - SQLFragment inner = queryService.getSelectSQL(tableInfo, columnMap.values(), null, null, -1, 0, false); + SQLFragment inner = queryService.getSelectBuilder(tableInfo).columns(columnMap.values()).buildSqlFragment(); // Insert COUNT String sampleCountName = tableInfo.getSqlDialect().makeLegalIdentifier("SampleCount"); From 328799c7cbdbc5c12bb4c6064998ab7a0962ca6a Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Mon, 1 Jun 2026 17:20:23 -0700 Subject: [PATCH 479/494] Update copyrights and licenses Original PR: https://github.com/LabKey/platform/pull/7706 #### Rationale Updates for 2026: https://github.com/LabKey/internal-issues/issues/1184 --- specimen/build.gradle | 15 +++++++++++++++ .../specimen/AmbiguousLocationException.java | 15 +++++++++++++++ .../src/org/labkey/specimen/RequestEventType.java | 15 +++++++++++++++ .../org/labkey/specimen/RequestedSpecimens.java | 15 +++++++++++++++ .../src/org/labkey/specimen/SpecimenColumns.java | 15 +++++++++++++++ .../specimen/SpecimenCommentAuditDomainKind.java | 15 +++++++++++++++ .../specimen/SpecimenCommentAuditEvent.java | 15 +++++++++++++++ .../specimen/SpecimenCommentAuditProvider.java | 2 +- .../src/org/labkey/specimen/SpecimenManager.java | 15 +++++++++++++++ .../src/org/labkey/specimen/SpecimenModule.java | 2 +- .../SpecimenRequestContainerListener.java | 15 +++++++++++++++ .../labkey/specimen/SpecimenRequestException.java | 15 +++++++++++++++ .../labkey/specimen/SpecimenRequestManager.java | 15 +++++++++++++++ .../labkey/specimen/SpecimenRequestStatus.java | 2 +- .../org/labkey/specimen/SpecimenServiceImpl.java | 2 +- .../org/labkey/specimen/SpecimenTableManager.java | 15 +++++++++++++++ .../specimen/actions/ButtonBarLineBreak.java | 2 +- .../actions/HiddenFormInputGenerator.java | 15 +++++++++++++++ .../src/org/labkey/specimen/actions/IdForm.java | 15 +++++++++++++++ .../src/org/labkey/specimen/actions/IdTypes.java | 15 +++++++++++++++ .../labkey/specimen/actions/ManageReqsBean.java | 15 +++++++++++++++ .../specimen/actions/ManageRequestBean.java | 15 +++++++++++++++ .../specimen/actions/ManageRequestInputsBean.java | 15 +++++++++++++++ .../actions/ParticipantCommentAction.java | 2 +- .../specimen/actions/ParticipantCommentForm.java | 15 +++++++++++++++ .../specimen/actions/ReportConfigurationBean.java | 15 +++++++++++++++ .../actions/SelectSpecimenProviderBean.java | 15 +++++++++++++++ .../specimen/actions/ShowGroupMembersAction.java | 2 +- .../labkey/specimen/actions/ShowSearchAction.java | 2 +- .../actions/ShowUploadSpecimensAction.java | 2 +- .../specimen/actions/SpecimenApiController.java | 2 +- .../specimen/actions/SpecimenController.java | 15 +++++++++++++++ .../specimen/actions/SpecimenEventBean.java | 15 +++++++++++++++ .../specimen/actions/SpecimenHeaderBean.java | 15 +++++++++++++++ .../specimen/actions/SpecimenReportActions.java | 15 +++++++++++++++ .../specimen/actions/SpecimenSearchBean.java | 2 +- .../specimen/actions/SpecimenViewTypeForm.java | 15 +++++++++++++++ .../specimen/actions/SpecimensViewBean.java | 15 +++++++++++++++ .../actions/UpdateSpecimenCommentsBean.java | 15 +++++++++++++++ .../specimen/actions/ViewRequestsHeaderBean.java | 15 +++++++++++++++ .../specimen/importer/AbstractSpecimenTask.java | 2 +- .../importer/AbstractSpecimenTaskFactory.java | 2 +- .../importer/EditableSpecimenImporter.java | 2 +- .../importer/FileSystemSpecimenImportFile.java | 2 +- .../org/labkey/specimen/importer/ImportTypes.java | 15 +++++++++++++++ .../specimen/importer/ImportableColumn.java | 15 +++++++++++++++ .../importer/IteratorSpecimenImportFile.java | 2 +- .../specimen/importer/QueryBasedExport.java | 15 +++++++++++++++ .../QueryBasedSpecimenImportUploadTask.java | 15 +++++++++++++++ .../importer/QueryBasedSpecimenReloadConfig.java | 15 +++++++++++++++ .../importer/QueryBasedSpecimenTransform.java | 15 +++++++++++++++ .../importer/QueryBasedTransformTask.java | 15 +++++++++++++++ .../specimen/importer/RequestabilityManager.java | 2 +- .../specimen/importer/SimpleSpecimenImporter.java | 2 +- .../labkey/specimen/importer/SpecimenColumn.java | 15 +++++++++++++++ .../specimen/importer/SpecimenImportFile.java | 2 +- .../specimen/importer/SpecimenImporter.java | 2 +- .../specimen/importer/SpecimenSchemaImporter.java | 2 +- .../importer/SpecimenSettingsImporter.java | 2 +- .../specimen/importer/SpecimenTableType.java | 15 +++++++++++++++ .../org/labkey/specimen/importer/TargetTable.java | 15 +++++++++++++++ .../org/labkey/specimen/model/AdditiveType.java | 2 +- .../org/labkey/specimen/model/DerivativeType.java | 2 +- .../model/ExtendedSpecimenRequestView.java | 2 +- .../org/labkey/specimen/model/PrimaryType.java | 2 +- .../specimen/model/SpecimenRequestActor.java | 2 +- .../specimen/model/SpecimenRequestEvent.java | 2 +- .../specimen/model/SpecimenRequestEventType.java | 2 +- .../specimen/model/SpecimenTypeSummary.java | 2 +- .../specimen/model/SpecimenTypeSummaryRow.java | 2 +- .../ActorNotificationRecipientSet.java | 2 +- .../notifications/DefaultRequestNotification.java | 2 +- .../notifications/NotificationRecipientSet.java | 2 +- .../pipeline/FileAnalysisSpecimenTask.java | 15 +++++++++++++++ .../labkey/specimen/pipeline/SpecimenArchive.java | 2 +- .../labkey/specimen/pipeline/SpecimenBatch.java | 2 +- .../specimen/pipeline/SpecimenJobSupport.java | 2 +- .../specimen/pipeline/SpecimenPipeline.java | 15 +++++++++++++++ .../specimen/pipeline/SpecimenReloadJob.java | 2 +- .../pipeline/SpecimenReloadJobSupport.java | 2 +- .../specimen/pipeline/SpecimenReloadTask.java | 2 +- .../specimen/pipeline/StandaloneSpecimenTask.java | 2 +- .../specimen/query/BaseSpecimenPivotTable.java | 2 +- .../specimen/query/BaseSpecimenQueryView.java | 2 +- .../specimen/query/SpecimenEventQueryView.java | 2 +- .../query/SpecimenPivotByDerivativeType.java | 2 +- .../query/SpecimenPivotByPrimaryType.java | 2 +- .../query/SpecimenPivotByRequestingLocation.java | 2 +- .../labkey/specimen/query/SpecimenQueryView.java | 2 +- .../specimen/query/SpecimenReportQuery.java | 2 +- .../query/SpecimenRequestDisplayColumn.java | 2 +- .../specimen/query/SpecimenRequestQueryView.java | 2 +- .../specimen/query/SpecimenUpdateService.java | 2 +- .../report/RequestSummaryByVisitType.java | 15 +++++++++++++++ .../specimen/report/SpecimenCountSummary.java | 2 +- .../report/SpecimenDetailQueryHelper.java | 15 +++++++++++++++ .../specimen/report/SpecimenReportCellData.java | 2 +- .../report/SpecimenReportExcelWriter.java | 2 +- .../specimen/report/SpecimenReportManager.java | 15 +++++++++++++++ .../specimen/report/SpecimenReportTitle.java | 2 +- .../specimen/report/SpecimenTypeBeanProperty.java | 15 +++++++++++++++ .../labkey/specimen/report/SpecimenTypeLevel.java | 15 +++++++++++++++ .../specimen/report/SpecimenTypeVisitReport.java | 2 +- .../specimen/report/SpecimenVisitReport.java | 2 +- .../report/SpecimenVisitReportAction.java | 15 +++++++++++++++ .../report/SpecimenVisitReportParameters.java | 2 +- .../report/SummaryByVisitParticipant.java | 15 +++++++++++++++ .../specimen/report/SummaryByVisitType.java | 15 +++++++++++++++ .../report/participant/ParticipantSiteReport.java | 2 +- .../participant/ParticipantSiteReportFactory.java | 2 +- .../ParticipantSummaryReportFactory.java | 2 +- .../participant/ParticipantTypeReportFactory.java | 2 +- .../participant/ParticipantVisitReport.java | 2 +- .../report/request/BaseRequestReportFactory.java | 2 +- .../request/RequestEnrollmentLocationReport.java | 2 +- .../RequestEnrollmentSiteReportFactory.java | 2 +- .../report/request/RequestLocationReport.java | 2 +- .../request/RequestLocationReportFactory.java | 2 +- .../report/request/RequestParticipantReport.java | 2 +- .../request/RequestParticipantReportFactory.java | 2 +- .../specimen/report/request/RequestReport.java | 2 +- .../report/request/RequestReportFactory.java | 2 +- .../report/specimentype/TypeCohortReport.java | 2 +- .../specimentype/TypeCohortReportFactory.java | 2 +- .../TypeParticipantReportFactory.java | 2 +- .../report/specimentype/TypeReportFactory.java | 2 +- .../specimentype/TypeSummaryReportFactory.java | 2 +- .../specimen/requirements/DefaultActor.java | 2 +- .../specimen/requirements/DefaultRequirement.java | 2 +- .../requirements/DefaultRequirementProvider.java | 2 +- .../labkey/specimen/requirements/Requirement.java | 2 +- .../specimen/requirements/RequirementActor.java | 2 +- .../specimen/requirements/RequirementOwner.java | 2 +- .../requirements/RequirementProvider.java | 2 +- .../specimen/requirements/RequirementType.java | 2 +- .../specimen/requirements/SpecimenRequest.java | 2 +- .../requirements/SpecimenRequestRequirement.java | 2 +- .../SpecimenRequestRequirementProvider.java | 2 +- .../SpecimenRequestRequirementType.java | 2 +- .../permissions/LockSpecimensPermission.java | 2 +- .../ManageDisplaySettingsPermission.java | 2 +- .../ManageNewRequestFormPermission.java | 2 +- .../ManageNotificationsPermission.java | 2 +- .../ManageRequestRequirementsPermission.java | 2 +- .../ManageRequestStatusesPermission.java | 2 +- .../permissions/ManageRequestsPermission.java | 2 +- .../ManageSpecimenActorsPermission.java | 2 +- .../SetSpecimenCommentsPermission.java | 2 +- .../security/roles/AbstractSpecimenRole.java | 2 +- .../security/roles/SpecimenCoordinatorRole.java | 2 +- .../security/roles/SpecimenRequesterRole.java | 2 +- .../labkey/specimen/settings/DisplaySettings.java | 2 +- .../specimen/settings/RepositorySettings.java | 2 +- .../settings/RequestNotificationSettings.java | 2 +- .../labkey/specimen/settings/SettingsManager.java | 15 +++++++++++++++ .../labkey/specimen/settings/StatusSettings.java | 2 +- .../labkey/specimen/view/ManageSpecimenView.java | 15 +++++++++++++++ .../labkey/specimen/view/NotificationBean.java | 15 +++++++++++++++ .../view/SpecimenReportWebPartFactory.java | 15 +++++++++++++++ .../SpecimenRequestNotificationEmailTemplate.java | 2 +- .../specimen/view/SpecimenSearchWebPart.java | 2 +- .../view/SpecimenSearchWebPartFactory.java | 15 +++++++++++++++ .../view/SpecimenToolsWebPartFactory.java | 15 +++++++++++++++ .../org/labkey/specimen/view/SpecimenWebPart.java | 2 +- .../specimen/view/SpecimenWebPartFactory.java | 15 +++++++++++++++ .../org/labkey/specimen/view/autoReportList.jsp | 2 +- .../org/labkey/specimen/view/chooseImporter.jsp | 15 +++++++++++++-- .../specimen/view/configRequestabilityRules.jsp | 2 +- .../specimen/view/configurationRequired.jsp | 2 +- .../labkey/specimen/view/configureQueryImport.jsp | 15 +++++++++++++-- .../view/customizeSpecimenReportWebPart.jsp | 2 +- .../src/org/labkey/specimen/view/groupMembers.jsp | 2 +- .../org/labkey/specimen/view/importSpecimens.jsp | 2 +- .../org/labkey/specimen/view/labSpecimenLists.jsp | 2 +- .../org/labkey/specimen/view/manageActorOrder.jsp | 2 +- .../src/org/labkey/specimen/view/manageActors.jsp | 2 +- .../org/labkey/specimen/view/manageComments.jsp | 2 +- .../labkey/specimen/view/manageDefaultReqs.jsp | 2 +- .../org/labkey/specimen/view/manageDisplay.jsp | 2 +- .../labkey/specimen/view/manageLocationTypes.jsp | 2 +- .../labkey/specimen/view/manageNotifications.jsp | 2 +- .../specimen/view/manageRepositorySettings.jsp | 2 +- .../org/labkey/specimen/view/manageRequest.jsp | 2 +- .../labkey/specimen/view/manageRequestInputs.jsp | 2 +- .../labkey/specimen/view/manageRequestStatus.jsp | 2 +- .../labkey/specimen/view/manageRequirement.jsp | 2 +- .../specimen/view/manageSpecimenWebPart.jsp | 2 +- .../org/labkey/specimen/view/manageSpecimens.jsp | 2 +- .../labkey/specimen/view/manageStatusOrder.jsp | 2 +- .../org/labkey/specimen/view/manageStatuses.jsp | 2 +- .../org/labkey/specimen/view/relevantRequests.jsp | 2 +- .../org/labkey/specimen/view/requestSpecimens.jsp | 2 +- specimen/src/org/labkey/specimen/view/search.jsp | 2 +- .../specimen/view/selectSpecimenProvider.jsp | 2 +- .../src/org/labkey/specimen/view/specimen.jsp | 2 +- .../org/labkey/specimen/view/specimenHeader.jsp | 2 +- .../labkey/specimen/view/specimenVisitReport.jsp | 2 +- .../org/labkey/specimen/view/updateComments.jsp | 2 +- .../specimen/view/uploadSimpleSpecimens.jsp | 2 +- .../labkey/specimen/view/viewRequestsHeader.jsp | 2 +- specimen/src/org/labkey/specimen/view/webPart.jsp | 2 +- .../specimen/writer/AbstractSpecimenWriter.java | 2 +- .../specimen/writer/LocationSpecimenWriter.java | 2 +- .../specimen/writer/SpecimenArchiveDataTypes.java | 15 +++++++++++++++ .../specimen/writer/SpecimenArchiveWriter.java | 2 +- .../specimen/writer/SpecimenSettingsWriter.java | 2 +- .../labkey/specimen/writer/SpecimenWriter.java | 2 +- .../specimen/writer/StandardSpecimenWriter.java | 2 +- .../specimen/ConfigureSpecimenImportPage.java | 15 +++++++++++++++ .../specimen/QueryBasedSpecimenImportTest.java | 15 +++++++++++++++ .../test/tests/study/SpecimenMergeTest.java | 2 +- .../test/tests/study/SpecimenReplaceTest.java | 2 +- 212 files changed, 1062 insertions(+), 155 deletions(-) diff --git a/specimen/build.gradle b/specimen/build.gradle index 4623e33d..c746886c 100644 --- a/specimen/build.gradle +++ b/specimen/build.gradle @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2021-2026 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. + */ import org.labkey.gradle.util.BuildUtils plugins { diff --git a/specimen/src/org/labkey/specimen/AmbiguousLocationException.java b/specimen/src/org/labkey/specimen/AmbiguousLocationException.java index 756ac71a..acc9a1d1 100644 --- a/specimen/src/org/labkey/specimen/AmbiguousLocationException.java +++ b/specimen/src/org/labkey/specimen/AmbiguousLocationException.java @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2021-2026 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.specimen; import org.labkey.api.data.Container; diff --git a/specimen/src/org/labkey/specimen/RequestEventType.java b/specimen/src/org/labkey/specimen/RequestEventType.java index e2c42d38..cca053f3 100644 --- a/specimen/src/org/labkey/specimen/RequestEventType.java +++ b/specimen/src/org/labkey/specimen/RequestEventType.java @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2021-2026 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.specimen; public enum RequestEventType diff --git a/specimen/src/org/labkey/specimen/RequestedSpecimens.java b/specimen/src/org/labkey/specimen/RequestedSpecimens.java index 3662eb4c..3920219c 100644 --- a/specimen/src/org/labkey/specimen/RequestedSpecimens.java +++ b/specimen/src/org/labkey/specimen/RequestedSpecimens.java @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2021-2026 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.specimen; import org.labkey.api.data.Container; diff --git a/specimen/src/org/labkey/specimen/SpecimenColumns.java b/specimen/src/org/labkey/specimen/SpecimenColumns.java index 613b660f..3e214989 100644 --- a/specimen/src/org/labkey/specimen/SpecimenColumns.java +++ b/specimen/src/org/labkey/specimen/SpecimenColumns.java @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2021-2026 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.specimen; import org.labkey.specimen.importer.ImportTypes; diff --git a/specimen/src/org/labkey/specimen/SpecimenCommentAuditDomainKind.java b/specimen/src/org/labkey/specimen/SpecimenCommentAuditDomainKind.java index c3ec485d..2964ad31 100644 --- a/specimen/src/org/labkey/specimen/SpecimenCommentAuditDomainKind.java +++ b/specimen/src/org/labkey/specimen/SpecimenCommentAuditDomainKind.java @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2021-2026 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.specimen; import org.labkey.api.audit.query.AbstractAuditDomainKind; diff --git a/specimen/src/org/labkey/specimen/SpecimenCommentAuditEvent.java b/specimen/src/org/labkey/specimen/SpecimenCommentAuditEvent.java index 994eebd8..b029df14 100644 --- a/specimen/src/org/labkey/specimen/SpecimenCommentAuditEvent.java +++ b/specimen/src/org/labkey/specimen/SpecimenCommentAuditEvent.java @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2021-2026 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.specimen; import org.labkey.api.audit.AuditTypeEvent; diff --git a/specimen/src/org/labkey/specimen/SpecimenCommentAuditProvider.java b/specimen/src/org/labkey/specimen/SpecimenCommentAuditProvider.java index f15aa3df..cb12be95 100644 --- a/specimen/src/org/labkey/specimen/SpecimenCommentAuditProvider.java +++ b/specimen/src/org/labkey/specimen/SpecimenCommentAuditProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 LabKey Corporation + * Copyright (c) 2021-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/SpecimenManager.java b/specimen/src/org/labkey/specimen/SpecimenManager.java index 0f53cbe8..8ef913b9 100644 --- a/specimen/src/org/labkey/specimen/SpecimenManager.java +++ b/specimen/src/org/labkey/specimen/SpecimenManager.java @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2021-2026 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.specimen; import org.jetbrains.annotations.NotNull; diff --git a/specimen/src/org/labkey/specimen/SpecimenModule.java b/specimen/src/org/labkey/specimen/SpecimenModule.java index 798e90c4..f4deed39 100644 --- a/specimen/src/org/labkey/specimen/SpecimenModule.java +++ b/specimen/src/org/labkey/specimen/SpecimenModule.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 LabKey Corporation + * Copyright (c) 2021-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/SpecimenRequestContainerListener.java b/specimen/src/org/labkey/specimen/SpecimenRequestContainerListener.java index 96865519..8f04c51e 100644 --- a/specimen/src/org/labkey/specimen/SpecimenRequestContainerListener.java +++ b/specimen/src/org/labkey/specimen/SpecimenRequestContainerListener.java @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2022-2026 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.specimen; import org.labkey.api.data.Container; diff --git a/specimen/src/org/labkey/specimen/SpecimenRequestException.java b/specimen/src/org/labkey/specimen/SpecimenRequestException.java index 9f1e6831..ea05a1cb 100644 --- a/specimen/src/org/labkey/specimen/SpecimenRequestException.java +++ b/specimen/src/org/labkey/specimen/SpecimenRequestException.java @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2021-2026 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.specimen; public class SpecimenRequestException extends RuntimeException diff --git a/specimen/src/org/labkey/specimen/SpecimenRequestManager.java b/specimen/src/org/labkey/specimen/SpecimenRequestManager.java index a26d066e..5107f286 100644 --- a/specimen/src/org/labkey/specimen/SpecimenRequestManager.java +++ b/specimen/src/org/labkey/specimen/SpecimenRequestManager.java @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2021-2026 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.specimen; import org.apache.commons.collections4.comparators.ComparableComparator; diff --git a/specimen/src/org/labkey/specimen/SpecimenRequestStatus.java b/specimen/src/org/labkey/specimen/SpecimenRequestStatus.java index b67145db..3b94626f 100644 --- a/specimen/src/org/labkey/specimen/SpecimenRequestStatus.java +++ b/specimen/src/org/labkey/specimen/SpecimenRequestStatus.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 LabKey Corporation + * Copyright (c) 2008-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/SpecimenServiceImpl.java b/specimen/src/org/labkey/specimen/SpecimenServiceImpl.java index 6979b368..75c36cb8 100644 --- a/specimen/src/org/labkey/specimen/SpecimenServiceImpl.java +++ b/specimen/src/org/labkey/specimen/SpecimenServiceImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008-2019 LabKey Corporation + * Copyright (c) 2008-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/SpecimenTableManager.java b/specimen/src/org/labkey/specimen/SpecimenTableManager.java index c891966e..8d4688af 100644 --- a/specimen/src/org/labkey/specimen/SpecimenTableManager.java +++ b/specimen/src/org/labkey/specimen/SpecimenTableManager.java @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2021-2026 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.specimen; import org.labkey.api.data.Container; diff --git a/specimen/src/org/labkey/specimen/actions/ButtonBarLineBreak.java b/specimen/src/org/labkey/specimen/actions/ButtonBarLineBreak.java index 51a1c2a2..d632f476 100644 --- a/specimen/src/org/labkey/specimen/actions/ButtonBarLineBreak.java +++ b/specimen/src/org/labkey/specimen/actions/ButtonBarLineBreak.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008 LabKey Corporation + * Copyright (c) 2008-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/actions/HiddenFormInputGenerator.java b/specimen/src/org/labkey/specimen/actions/HiddenFormInputGenerator.java index 7f6bfa27..884c773c 100644 --- a/specimen/src/org/labkey/specimen/actions/HiddenFormInputGenerator.java +++ b/specimen/src/org/labkey/specimen/actions/HiddenFormInputGenerator.java @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2021-2026 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.specimen.actions; import org.labkey.api.view.ViewContext; diff --git a/specimen/src/org/labkey/specimen/actions/IdForm.java b/specimen/src/org/labkey/specimen/actions/IdForm.java index 33d62456..23ca74e7 100644 --- a/specimen/src/org/labkey/specimen/actions/IdForm.java +++ b/specimen/src/org/labkey/specimen/actions/IdForm.java @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2021-2026 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.specimen.actions; import org.labkey.api.action.ReturnUrlForm; diff --git a/specimen/src/org/labkey/specimen/actions/IdTypes.java b/specimen/src/org/labkey/specimen/actions/IdTypes.java index f8708571..2999b872 100644 --- a/specimen/src/org/labkey/specimen/actions/IdTypes.java +++ b/specimen/src/org/labkey/specimen/actions/IdTypes.java @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2021-2026 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.specimen.actions; public enum IdTypes diff --git a/specimen/src/org/labkey/specimen/actions/ManageReqsBean.java b/specimen/src/org/labkey/specimen/actions/ManageReqsBean.java index 49b627bc..fea7242f 100644 --- a/specimen/src/org/labkey/specimen/actions/ManageReqsBean.java +++ b/specimen/src/org/labkey/specimen/actions/ManageReqsBean.java @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2021-2026 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.specimen.actions; import org.labkey.api.data.Container; diff --git a/specimen/src/org/labkey/specimen/actions/ManageRequestBean.java b/specimen/src/org/labkey/specimen/actions/ManageRequestBean.java index d604e866..8be85093 100644 --- a/specimen/src/org/labkey/specimen/actions/ManageRequestBean.java +++ b/specimen/src/org/labkey/specimen/actions/ManageRequestBean.java @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2021-2026 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.specimen.actions; import org.labkey.api.collections.IntHashMap; diff --git a/specimen/src/org/labkey/specimen/actions/ManageRequestInputsBean.java b/specimen/src/org/labkey/specimen/actions/ManageRequestInputsBean.java index a29b36e0..ce8ba5d3 100644 --- a/specimen/src/org/labkey/specimen/actions/ManageRequestInputsBean.java +++ b/specimen/src/org/labkey/specimen/actions/ManageRequestInputsBean.java @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2021-2026 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.specimen.actions; import org.labkey.api.data.Container; diff --git a/specimen/src/org/labkey/specimen/actions/ParticipantCommentAction.java b/specimen/src/org/labkey/specimen/actions/ParticipantCommentAction.java index 17c2438a..e259eebd 100644 --- a/specimen/src/org/labkey/specimen/actions/ParticipantCommentAction.java +++ b/specimen/src/org/labkey/specimen/actions/ParticipantCommentAction.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 LabKey Corporation + * Copyright (c) 2009-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/actions/ParticipantCommentForm.java b/specimen/src/org/labkey/specimen/actions/ParticipantCommentForm.java index 16eed5f2..0d88d6f2 100644 --- a/specimen/src/org/labkey/specimen/actions/ParticipantCommentForm.java +++ b/specimen/src/org/labkey/specimen/actions/ParticipantCommentForm.java @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2021-2026 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.specimen.actions; import org.labkey.api.study.EditDatasetRowForm; diff --git a/specimen/src/org/labkey/specimen/actions/ReportConfigurationBean.java b/specimen/src/org/labkey/specimen/actions/ReportConfigurationBean.java index 54c6edc4..b943deae 100644 --- a/specimen/src/org/labkey/specimen/actions/ReportConfigurationBean.java +++ b/specimen/src/org/labkey/specimen/actions/ReportConfigurationBean.java @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2021-2026 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.specimen.actions; import org.labkey.api.query.CustomView; diff --git a/specimen/src/org/labkey/specimen/actions/SelectSpecimenProviderBean.java b/specimen/src/org/labkey/specimen/actions/SelectSpecimenProviderBean.java index 2e371255..1e9a0300 100644 --- a/specimen/src/org/labkey/specimen/actions/SelectSpecimenProviderBean.java +++ b/specimen/src/org/labkey/specimen/actions/SelectSpecimenProviderBean.java @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2021-2026 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.specimen.actions; import org.labkey.api.specimen.location.LocationImpl; diff --git a/specimen/src/org/labkey/specimen/actions/ShowGroupMembersAction.java b/specimen/src/org/labkey/specimen/actions/ShowGroupMembersAction.java index ddf84246..10b97d04 100644 --- a/specimen/src/org/labkey/specimen/actions/ShowGroupMembersAction.java +++ b/specimen/src/org/labkey/specimen/actions/ShowGroupMembersAction.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 LabKey Corporation + * Copyright (c) 2009-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/actions/ShowSearchAction.java b/specimen/src/org/labkey/specimen/actions/ShowSearchAction.java index 1bc81efb..70c45412 100644 --- a/specimen/src/org/labkey/specimen/actions/ShowSearchAction.java +++ b/specimen/src/org/labkey/specimen/actions/ShowSearchAction.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 LabKey Corporation + * Copyright (c) 2009-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/actions/ShowUploadSpecimensAction.java b/specimen/src/org/labkey/specimen/actions/ShowUploadSpecimensAction.java index 5c8334e3..109fb030 100644 --- a/specimen/src/org/labkey/specimen/actions/ShowUploadSpecimensAction.java +++ b/specimen/src/org/labkey/specimen/actions/ShowUploadSpecimensAction.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 LabKey Corporation + * Copyright (c) 2009-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/actions/SpecimenApiController.java b/specimen/src/org/labkey/specimen/actions/SpecimenApiController.java index e9a7dd6d..0ea3423f 100644 --- a/specimen/src/org/labkey/specimen/actions/SpecimenApiController.java +++ b/specimen/src/org/labkey/specimen/actions/SpecimenApiController.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 LabKey Corporation + * Copyright (c) 2009-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/actions/SpecimenController.java b/specimen/src/org/labkey/specimen/actions/SpecimenController.java index 5fb8abaa..22b99901 100644 --- a/specimen/src/org/labkey/specimen/actions/SpecimenController.java +++ b/specimen/src/org/labkey/specimen/actions/SpecimenController.java @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2021-2026 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.specimen.actions; import jakarta.mail.Address; diff --git a/specimen/src/org/labkey/specimen/actions/SpecimenEventBean.java b/specimen/src/org/labkey/specimen/actions/SpecimenEventBean.java index 9eb62970..1246da6f 100644 --- a/specimen/src/org/labkey/specimen/actions/SpecimenEventBean.java +++ b/specimen/src/org/labkey/specimen/actions/SpecimenEventBean.java @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2021-2026 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.specimen.actions; import org.labkey.api.action.ReturnUrlForm; diff --git a/specimen/src/org/labkey/specimen/actions/SpecimenHeaderBean.java b/specimen/src/org/labkey/specimen/actions/SpecimenHeaderBean.java index fe32d12d..996a7b63 100644 --- a/specimen/src/org/labkey/specimen/actions/SpecimenHeaderBean.java +++ b/specimen/src/org/labkey/specimen/actions/SpecimenHeaderBean.java @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2021-2026 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.specimen.actions; import org.labkey.api.data.ColumnInfo; diff --git a/specimen/src/org/labkey/specimen/actions/SpecimenReportActions.java b/specimen/src/org/labkey/specimen/actions/SpecimenReportActions.java index 05b8648d..86ca94bb 100644 --- a/specimen/src/org/labkey/specimen/actions/SpecimenReportActions.java +++ b/specimen/src/org/labkey/specimen/actions/SpecimenReportActions.java @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2021-2026 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.specimen.actions; import org.labkey.api.security.RequiresPermission; diff --git a/specimen/src/org/labkey/specimen/actions/SpecimenSearchBean.java b/specimen/src/org/labkey/specimen/actions/SpecimenSearchBean.java index 4c88a25b..842ac15d 100644 --- a/specimen/src/org/labkey/specimen/actions/SpecimenSearchBean.java +++ b/specimen/src/org/labkey/specimen/actions/SpecimenSearchBean.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 LabKey Corporation + * Copyright (c) 2010-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/actions/SpecimenViewTypeForm.java b/specimen/src/org/labkey/specimen/actions/SpecimenViewTypeForm.java index a170962d..e2926988 100644 --- a/specimen/src/org/labkey/specimen/actions/SpecimenViewTypeForm.java +++ b/specimen/src/org/labkey/specimen/actions/SpecimenViewTypeForm.java @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2021-2026 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.specimen.actions; import org.labkey.api.action.QueryViewAction; diff --git a/specimen/src/org/labkey/specimen/actions/SpecimensViewBean.java b/specimen/src/org/labkey/specimen/actions/SpecimensViewBean.java index 5cba6f1b..33336aae 100644 --- a/specimen/src/org/labkey/specimen/actions/SpecimensViewBean.java +++ b/specimen/src/org/labkey/specimen/actions/SpecimensViewBean.java @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2021-2026 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.specimen.actions; import org.labkey.api.specimen.Vial; diff --git a/specimen/src/org/labkey/specimen/actions/UpdateSpecimenCommentsBean.java b/specimen/src/org/labkey/specimen/actions/UpdateSpecimenCommentsBean.java index c4dce316..c89a842c 100644 --- a/specimen/src/org/labkey/specimen/actions/UpdateSpecimenCommentsBean.java +++ b/specimen/src/org/labkey/specimen/actions/UpdateSpecimenCommentsBean.java @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2021-2026 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.specimen.actions; import org.apache.commons.lang3.time.DateUtils; diff --git a/specimen/src/org/labkey/specimen/actions/ViewRequestsHeaderBean.java b/specimen/src/org/labkey/specimen/actions/ViewRequestsHeaderBean.java index 83489d00..f1e97679 100644 --- a/specimen/src/org/labkey/specimen/actions/ViewRequestsHeaderBean.java +++ b/specimen/src/org/labkey/specimen/actions/ViewRequestsHeaderBean.java @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2021-2026 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.specimen.actions; import org.labkey.api.view.ViewContext; diff --git a/specimen/src/org/labkey/specimen/importer/AbstractSpecimenTask.java b/specimen/src/org/labkey/specimen/importer/AbstractSpecimenTask.java index e6192567..7b2e21f4 100644 --- a/specimen/src/org/labkey/specimen/importer/AbstractSpecimenTask.java +++ b/specimen/src/org/labkey/specimen/importer/AbstractSpecimenTask.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2017 LabKey Corporation + * Copyright (c) 2009-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/importer/AbstractSpecimenTaskFactory.java b/specimen/src/org/labkey/specimen/importer/AbstractSpecimenTaskFactory.java index 9084ee69..ebd73cfa 100644 --- a/specimen/src/org/labkey/specimen/importer/AbstractSpecimenTaskFactory.java +++ b/specimen/src/org/labkey/specimen/importer/AbstractSpecimenTaskFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2010 LabKey Corporation + * Copyright (c) 2009-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/importer/EditableSpecimenImporter.java b/specimen/src/org/labkey/specimen/importer/EditableSpecimenImporter.java index 037989a9..a78b3d32 100644 --- a/specimen/src/org/labkey/specimen/importer/EditableSpecimenImporter.java +++ b/specimen/src/org/labkey/specimen/importer/EditableSpecimenImporter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2018 LabKey Corporation + * Copyright (c) 2013-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/importer/FileSystemSpecimenImportFile.java b/specimen/src/org/labkey/specimen/importer/FileSystemSpecimenImportFile.java index 9ca6c21e..b87ad73f 100644 --- a/specimen/src/org/labkey/specimen/importer/FileSystemSpecimenImportFile.java +++ b/specimen/src/org/labkey/specimen/importer/FileSystemSpecimenImportFile.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2014 LabKey Corporation + * Copyright (c) 2013-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/importer/ImportTypes.java b/specimen/src/org/labkey/specimen/importer/ImportTypes.java index 6e91ea1a..34615d80 100644 --- a/specimen/src/org/labkey/specimen/importer/ImportTypes.java +++ b/specimen/src/org/labkey/specimen/importer/ImportTypes.java @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2021-2026 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.specimen.importer; import org.labkey.api.specimen.SpecimenSchema; diff --git a/specimen/src/org/labkey/specimen/importer/ImportableColumn.java b/specimen/src/org/labkey/specimen/importer/ImportableColumn.java index c556a687..2fd7c5cb 100644 --- a/specimen/src/org/labkey/specimen/importer/ImportableColumn.java +++ b/specimen/src/org/labkey/specimen/importer/ImportableColumn.java @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2021-2026 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.specimen.importer; import org.jetbrains.annotations.Nullable; diff --git a/specimen/src/org/labkey/specimen/importer/IteratorSpecimenImportFile.java b/specimen/src/org/labkey/specimen/importer/IteratorSpecimenImportFile.java index b217abd3..1ce3fbcb 100644 --- a/specimen/src/org/labkey/specimen/importer/IteratorSpecimenImportFile.java +++ b/specimen/src/org/labkey/specimen/importer/IteratorSpecimenImportFile.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2018 LabKey Corporation + * Copyright (c) 2013-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/importer/QueryBasedExport.java b/specimen/src/org/labkey/specimen/importer/QueryBasedExport.java index 7dde054b..b093e46e 100644 --- a/specimen/src/org/labkey/specimen/importer/QueryBasedExport.java +++ b/specimen/src/org/labkey/specimen/importer/QueryBasedExport.java @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2026 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.specimen.importer; import org.labkey.api.action.NullSafeBindException; diff --git a/specimen/src/org/labkey/specimen/importer/QueryBasedSpecimenImportUploadTask.java b/specimen/src/org/labkey/specimen/importer/QueryBasedSpecimenImportUploadTask.java index af9b7f64..2be5ede0 100644 --- a/specimen/src/org/labkey/specimen/importer/QueryBasedSpecimenImportUploadTask.java +++ b/specimen/src/org/labkey/specimen/importer/QueryBasedSpecimenImportUploadTask.java @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2026 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.specimen.importer; import org.apache.logging.log4j.Logger; diff --git a/specimen/src/org/labkey/specimen/importer/QueryBasedSpecimenReloadConfig.java b/specimen/src/org/labkey/specimen/importer/QueryBasedSpecimenReloadConfig.java index 396d63c6..09a43249 100644 --- a/specimen/src/org/labkey/specimen/importer/QueryBasedSpecimenReloadConfig.java +++ b/specimen/src/org/labkey/specimen/importer/QueryBasedSpecimenReloadConfig.java @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2026 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.specimen.importer; import org.labkey.api.study.SpecimenTransform; diff --git a/specimen/src/org/labkey/specimen/importer/QueryBasedSpecimenTransform.java b/specimen/src/org/labkey/specimen/importer/QueryBasedSpecimenTransform.java index 1e92c7b5..d98a6052 100644 --- a/specimen/src/org/labkey/specimen/importer/QueryBasedSpecimenTransform.java +++ b/specimen/src/org/labkey/specimen/importer/QueryBasedSpecimenTransform.java @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2026 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.specimen.importer; import org.jetbrains.annotations.Nullable; diff --git a/specimen/src/org/labkey/specimen/importer/QueryBasedTransformTask.java b/specimen/src/org/labkey/specimen/importer/QueryBasedTransformTask.java index 90a61d1c..e05e6341 100644 --- a/specimen/src/org/labkey/specimen/importer/QueryBasedTransformTask.java +++ b/specimen/src/org/labkey/specimen/importer/QueryBasedTransformTask.java @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2026 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.specimen.importer; import org.apache.commons.lang3.StringUtils; diff --git a/specimen/src/org/labkey/specimen/importer/RequestabilityManager.java b/specimen/src/org/labkey/specimen/importer/RequestabilityManager.java index f9afa4a6..07bcfcce 100644 --- a/specimen/src/org/labkey/specimen/importer/RequestabilityManager.java +++ b/specimen/src/org/labkey/specimen/importer/RequestabilityManager.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2018 LabKey Corporation + * Copyright (c) 2010-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/importer/SimpleSpecimenImporter.java b/specimen/src/org/labkey/specimen/importer/SimpleSpecimenImporter.java index 09d4f5f5..684c6d0d 100644 --- a/specimen/src/org/labkey/specimen/importer/SimpleSpecimenImporter.java +++ b/specimen/src/org/labkey/specimen/importer/SimpleSpecimenImporter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008-2019 LabKey Corporation + * Copyright (c) 2008-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/importer/SpecimenColumn.java b/specimen/src/org/labkey/specimen/importer/SpecimenColumn.java index 2cec7dce..37d665a5 100644 --- a/specimen/src/org/labkey/specimen/importer/SpecimenColumn.java +++ b/specimen/src/org/labkey/specimen/importer/SpecimenColumn.java @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2021-2026 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.specimen.importer; import org.labkey.api.util.TimeOnlyDate; diff --git a/specimen/src/org/labkey/specimen/importer/SpecimenImportFile.java b/specimen/src/org/labkey/specimen/importer/SpecimenImportFile.java index 3ea37386..bf9dc95c 100644 --- a/specimen/src/org/labkey/specimen/importer/SpecimenImportFile.java +++ b/specimen/src/org/labkey/specimen/importer/SpecimenImportFile.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013 LabKey Corporation + * Copyright (c) 2013-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/importer/SpecimenImporter.java b/specimen/src/org/labkey/specimen/importer/SpecimenImporter.java index 26f620a5..41d186ad 100644 --- a/specimen/src/org/labkey/specimen/importer/SpecimenImporter.java +++ b/specimen/src/org/labkey/specimen/importer/SpecimenImporter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008-2019 LabKey Corporation + * Copyright (c) 2008-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/importer/SpecimenSchemaImporter.java b/specimen/src/org/labkey/specimen/importer/SpecimenSchemaImporter.java index 7ace1092..0589905c 100644 --- a/specimen/src/org/labkey/specimen/importer/SpecimenSchemaImporter.java +++ b/specimen/src/org/labkey/specimen/importer/SpecimenSchemaImporter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2018 LabKey Corporation + * Copyright (c) 2014-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/importer/SpecimenSettingsImporter.java b/specimen/src/org/labkey/specimen/importer/SpecimenSettingsImporter.java index cc6d224e..70024499 100644 --- a/specimen/src/org/labkey/specimen/importer/SpecimenSettingsImporter.java +++ b/specimen/src/org/labkey/specimen/importer/SpecimenSettingsImporter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2018 LabKey Corporation + * Copyright (c) 2013-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/importer/SpecimenTableType.java b/specimen/src/org/labkey/specimen/importer/SpecimenTableType.java index e150702c..5d519e91 100644 --- a/specimen/src/org/labkey/specimen/importer/SpecimenTableType.java +++ b/specimen/src/org/labkey/specimen/importer/SpecimenTableType.java @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2021-2026 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.specimen.importer; import java.util.Collection; diff --git a/specimen/src/org/labkey/specimen/importer/TargetTable.java b/specimen/src/org/labkey/specimen/importer/TargetTable.java index 3a39031c..60dd5c4b 100644 --- a/specimen/src/org/labkey/specimen/importer/TargetTable.java +++ b/specimen/src/org/labkey/specimen/importer/TargetTable.java @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2021-2026 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.specimen.importer; import java.util.ArrayList; diff --git a/specimen/src/org/labkey/specimen/model/AdditiveType.java b/specimen/src/org/labkey/specimen/model/AdditiveType.java index 86f64c09..42940f0a 100644 --- a/specimen/src/org/labkey/specimen/model/AdditiveType.java +++ b/specimen/src/org/labkey/specimen/model/AdditiveType.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2015 LabKey Corporation + * Copyright (c) 2009-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/model/DerivativeType.java b/specimen/src/org/labkey/specimen/model/DerivativeType.java index 621fb838..e55f6418 100644 --- a/specimen/src/org/labkey/specimen/model/DerivativeType.java +++ b/specimen/src/org/labkey/specimen/model/DerivativeType.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2015 LabKey Corporation + * Copyright (c) 2009-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/model/ExtendedSpecimenRequestView.java b/specimen/src/org/labkey/specimen/model/ExtendedSpecimenRequestView.java index 7eab75c5..96a9afca 100644 --- a/specimen/src/org/labkey/specimen/model/ExtendedSpecimenRequestView.java +++ b/specimen/src/org/labkey/specimen/model/ExtendedSpecimenRequestView.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013 LabKey Corporation + * Copyright (c) 2013-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/model/PrimaryType.java b/specimen/src/org/labkey/specimen/model/PrimaryType.java index 71f3632f..a23e91e5 100644 --- a/specimen/src/org/labkey/specimen/model/PrimaryType.java +++ b/specimen/src/org/labkey/specimen/model/PrimaryType.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2015 LabKey Corporation + * Copyright (c) 2009-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/model/SpecimenRequestActor.java b/specimen/src/org/labkey/specimen/model/SpecimenRequestActor.java index 8fcbadd9..b0a2d6a5 100644 --- a/specimen/src/org/labkey/specimen/model/SpecimenRequestActor.java +++ b/specimen/src/org/labkey/specimen/model/SpecimenRequestActor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 LabKey Corporation + * Copyright (c) 2008-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/model/SpecimenRequestEvent.java b/specimen/src/org/labkey/specimen/model/SpecimenRequestEvent.java index 7feedcce..06d7a6ce 100644 --- a/specimen/src/org/labkey/specimen/model/SpecimenRequestEvent.java +++ b/specimen/src/org/labkey/specimen/model/SpecimenRequestEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 LabKey Corporation + * Copyright (c) 2008-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/model/SpecimenRequestEventType.java b/specimen/src/org/labkey/specimen/model/SpecimenRequestEventType.java index ad10c6a8..44465008 100644 --- a/specimen/src/org/labkey/specimen/model/SpecimenRequestEventType.java +++ b/specimen/src/org/labkey/specimen/model/SpecimenRequestEventType.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017 LabKey Corporation + * Copyright (c) 2017-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/model/SpecimenTypeSummary.java b/specimen/src/org/labkey/specimen/model/SpecimenTypeSummary.java index ea196125..b53e1825 100644 --- a/specimen/src/org/labkey/specimen/model/SpecimenTypeSummary.java +++ b/specimen/src/org/labkey/specimen/model/SpecimenTypeSummary.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008-2019 LabKey Corporation + * Copyright (c) 2008-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/model/SpecimenTypeSummaryRow.java b/specimen/src/org/labkey/specimen/model/SpecimenTypeSummaryRow.java index cff05810..8902b4c8 100644 --- a/specimen/src/org/labkey/specimen/model/SpecimenTypeSummaryRow.java +++ b/specimen/src/org/labkey/specimen/model/SpecimenTypeSummaryRow.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008-2019 LabKey Corporation + * Copyright (c) 2008-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/notifications/ActorNotificationRecipientSet.java b/specimen/src/org/labkey/specimen/notifications/ActorNotificationRecipientSet.java index a11ad3ba..7fa97a66 100644 --- a/specimen/src/org/labkey/specimen/notifications/ActorNotificationRecipientSet.java +++ b/specimen/src/org/labkey/specimen/notifications/ActorNotificationRecipientSet.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 LabKey Corporation + * Copyright (c) 2008-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/notifications/DefaultRequestNotification.java b/specimen/src/org/labkey/specimen/notifications/DefaultRequestNotification.java index c11d67e1..cc13757d 100644 --- a/specimen/src/org/labkey/specimen/notifications/DefaultRequestNotification.java +++ b/specimen/src/org/labkey/specimen/notifications/DefaultRequestNotification.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 LabKey Corporation + * Copyright (c) 2008-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/notifications/NotificationRecipientSet.java b/specimen/src/org/labkey/specimen/notifications/NotificationRecipientSet.java index 24d2e019..67683db7 100644 --- a/specimen/src/org/labkey/specimen/notifications/NotificationRecipientSet.java +++ b/specimen/src/org/labkey/specimen/notifications/NotificationRecipientSet.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 LabKey Corporation + * Copyright (c) 2008-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/pipeline/FileAnalysisSpecimenTask.java b/specimen/src/org/labkey/specimen/pipeline/FileAnalysisSpecimenTask.java index 680c5b43..5427de29 100644 --- a/specimen/src/org/labkey/specimen/pipeline/FileAnalysisSpecimenTask.java +++ b/specimen/src/org/labkey/specimen/pipeline/FileAnalysisSpecimenTask.java @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2020-2026 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.specimen.pipeline; import org.apache.commons.lang3.BooleanUtils; diff --git a/specimen/src/org/labkey/specimen/pipeline/SpecimenArchive.java b/specimen/src/org/labkey/specimen/pipeline/SpecimenArchive.java index 781b8d86..5688e357 100644 --- a/specimen/src/org/labkey/specimen/pipeline/SpecimenArchive.java +++ b/specimen/src/org/labkey/specimen/pipeline/SpecimenArchive.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2014 LabKey Corporation + * Copyright (c) 2009-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/pipeline/SpecimenBatch.java b/specimen/src/org/labkey/specimen/pipeline/SpecimenBatch.java index 2cd5d288..7bb40fe1 100644 --- a/specimen/src/org/labkey/specimen/pipeline/SpecimenBatch.java +++ b/specimen/src/org/labkey/specimen/pipeline/SpecimenBatch.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008-2019 LabKey Corporation + * Copyright (c) 2008-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/pipeline/SpecimenJobSupport.java b/specimen/src/org/labkey/specimen/pipeline/SpecimenJobSupport.java index 198155df..17c43c12 100644 --- a/specimen/src/org/labkey/specimen/pipeline/SpecimenJobSupport.java +++ b/specimen/src/org/labkey/specimen/pipeline/SpecimenJobSupport.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2018 LabKey Corporation + * Copyright (c) 2009-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/pipeline/SpecimenPipeline.java b/specimen/src/org/labkey/specimen/pipeline/SpecimenPipeline.java index dad9ceec..ab1c81dd 100644 --- a/specimen/src/org/labkey/specimen/pipeline/SpecimenPipeline.java +++ b/specimen/src/org/labkey/specimen/pipeline/SpecimenPipeline.java @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2021-2026 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.specimen.pipeline; import org.labkey.api.module.Module; diff --git a/specimen/src/org/labkey/specimen/pipeline/SpecimenReloadJob.java b/specimen/src/org/labkey/specimen/pipeline/SpecimenReloadJob.java index 819dc54c..7c21cdc5 100644 --- a/specimen/src/org/labkey/specimen/pipeline/SpecimenReloadJob.java +++ b/specimen/src/org/labkey/specimen/pipeline/SpecimenReloadJob.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2018 LabKey Corporation + * Copyright (c) 2014-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/pipeline/SpecimenReloadJobSupport.java b/specimen/src/org/labkey/specimen/pipeline/SpecimenReloadJobSupport.java index 5732b0b6..d5d2affb 100644 --- a/specimen/src/org/labkey/specimen/pipeline/SpecimenReloadJobSupport.java +++ b/specimen/src/org/labkey/specimen/pipeline/SpecimenReloadJobSupport.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014 LabKey Corporation + * Copyright (c) 2014-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/pipeline/SpecimenReloadTask.java b/specimen/src/org/labkey/specimen/pipeline/SpecimenReloadTask.java index eade63b4..c2d4e7d0 100644 --- a/specimen/src/org/labkey/specimen/pipeline/SpecimenReloadTask.java +++ b/specimen/src/org/labkey/specimen/pipeline/SpecimenReloadTask.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2016 LabKey Corporation + * Copyright (c) 2014-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/pipeline/StandaloneSpecimenTask.java b/specimen/src/org/labkey/specimen/pipeline/StandaloneSpecimenTask.java index 381fe077..e76444ea 100644 --- a/specimen/src/org/labkey/specimen/pipeline/StandaloneSpecimenTask.java +++ b/specimen/src/org/labkey/specimen/pipeline/StandaloneSpecimenTask.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2014 LabKey Corporation + * Copyright (c) 2009-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/query/BaseSpecimenPivotTable.java b/specimen/src/org/labkey/specimen/query/BaseSpecimenPivotTable.java index 5d733c1a..eb9fdc2f 100644 --- a/specimen/src/org/labkey/specimen/query/BaseSpecimenPivotTable.java +++ b/specimen/src/org/labkey/specimen/query/BaseSpecimenPivotTable.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2019 LabKey Corporation + * Copyright (c) 2012-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/query/BaseSpecimenQueryView.java b/specimen/src/org/labkey/specimen/query/BaseSpecimenQueryView.java index 67e0e84e..146a44ad 100644 --- a/specimen/src/org/labkey/specimen/query/BaseSpecimenQueryView.java +++ b/specimen/src/org/labkey/specimen/query/BaseSpecimenQueryView.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008-2019 LabKey Corporation + * Copyright (c) 2008-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/query/SpecimenEventQueryView.java b/specimen/src/org/labkey/specimen/query/SpecimenEventQueryView.java index 4eeb8f6d..09e25233 100644 --- a/specimen/src/org/labkey/specimen/query/SpecimenEventQueryView.java +++ b/specimen/src/org/labkey/specimen/query/SpecimenEventQueryView.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008-2019 LabKey Corporation + * Copyright (c) 2008-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/query/SpecimenPivotByDerivativeType.java b/specimen/src/org/labkey/specimen/query/SpecimenPivotByDerivativeType.java index 58462695..a326aebb 100644 --- a/specimen/src/org/labkey/specimen/query/SpecimenPivotByDerivativeType.java +++ b/specimen/src/org/labkey/specimen/query/SpecimenPivotByDerivativeType.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2019 LabKey Corporation + * Copyright (c) 2012-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/query/SpecimenPivotByPrimaryType.java b/specimen/src/org/labkey/specimen/query/SpecimenPivotByPrimaryType.java index 49ad2c32..ee540633 100644 --- a/specimen/src/org/labkey/specimen/query/SpecimenPivotByPrimaryType.java +++ b/specimen/src/org/labkey/specimen/query/SpecimenPivotByPrimaryType.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2019 LabKey Corporation + * Copyright (c) 2012-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/query/SpecimenPivotByRequestingLocation.java b/specimen/src/org/labkey/specimen/query/SpecimenPivotByRequestingLocation.java index ba5b8c8d..e9fe4b16 100644 --- a/specimen/src/org/labkey/specimen/query/SpecimenPivotByRequestingLocation.java +++ b/specimen/src/org/labkey/specimen/query/SpecimenPivotByRequestingLocation.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2019 LabKey Corporation + * Copyright (c) 2012-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/query/SpecimenQueryView.java b/specimen/src/org/labkey/specimen/query/SpecimenQueryView.java index 151d7875..e8970fda 100644 --- a/specimen/src/org/labkey/specimen/query/SpecimenQueryView.java +++ b/specimen/src/org/labkey/specimen/query/SpecimenQueryView.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008-2019 LabKey Corporation + * Copyright (c) 2008-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/query/SpecimenReportQuery.java b/specimen/src/org/labkey/specimen/query/SpecimenReportQuery.java index 6a455c83..451a8ce2 100644 --- a/specimen/src/org/labkey/specimen/query/SpecimenReportQuery.java +++ b/specimen/src/org/labkey/specimen/query/SpecimenReportQuery.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2019 LabKey Corporation + * Copyright (c) 2025-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/query/SpecimenRequestDisplayColumn.java b/specimen/src/org/labkey/specimen/query/SpecimenRequestDisplayColumn.java index b68c7d0c..e299bcfb 100644 --- a/specimen/src/org/labkey/specimen/query/SpecimenRequestDisplayColumn.java +++ b/specimen/src/org/labkey/specimen/query/SpecimenRequestDisplayColumn.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2014 LabKey Corporation + * Copyright (c) 2009-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/query/SpecimenRequestQueryView.java b/specimen/src/org/labkey/specimen/query/SpecimenRequestQueryView.java index 6799b910..cef34526 100644 --- a/specimen/src/org/labkey/specimen/query/SpecimenRequestQueryView.java +++ b/specimen/src/org/labkey/specimen/query/SpecimenRequestQueryView.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008-2019 LabKey Corporation + * Copyright (c) 2008-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/query/SpecimenUpdateService.java b/specimen/src/org/labkey/specimen/query/SpecimenUpdateService.java index e42a9afa..453c3863 100644 --- a/specimen/src/org/labkey/specimen/query/SpecimenUpdateService.java +++ b/specimen/src/org/labkey/specimen/query/SpecimenUpdateService.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2018 LabKey Corporation + * Copyright (c) 2013-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/report/RequestSummaryByVisitType.java b/specimen/src/org/labkey/specimen/report/RequestSummaryByVisitType.java index b623ecb2..5ab88441 100644 --- a/specimen/src/org/labkey/specimen/report/RequestSummaryByVisitType.java +++ b/specimen/src/org/labkey/specimen/report/RequestSummaryByVisitType.java @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2021-2026 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.specimen.report; public class RequestSummaryByVisitType extends SummaryByVisitType diff --git a/specimen/src/org/labkey/specimen/report/SpecimenCountSummary.java b/specimen/src/org/labkey/specimen/report/SpecimenCountSummary.java index 06fd68c8..96475aef 100644 --- a/specimen/src/org/labkey/specimen/report/SpecimenCountSummary.java +++ b/specimen/src/org/labkey/specimen/report/SpecimenCountSummary.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 LabKey Corporation + * Copyright (c) 2008-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/report/SpecimenDetailQueryHelper.java b/specimen/src/org/labkey/specimen/report/SpecimenDetailQueryHelper.java index 33f19022..c9e36898 100644 --- a/specimen/src/org/labkey/specimen/report/SpecimenDetailQueryHelper.java +++ b/specimen/src/org/labkey/specimen/report/SpecimenDetailQueryHelper.java @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2021-2026 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.specimen.report; import org.labkey.api.data.SQLFragment; diff --git a/specimen/src/org/labkey/specimen/report/SpecimenReportCellData.java b/specimen/src/org/labkey/specimen/report/SpecimenReportCellData.java index 2a87a79b..e8f85f66 100644 --- a/specimen/src/org/labkey/specimen/report/SpecimenReportCellData.java +++ b/specimen/src/org/labkey/specimen/report/SpecimenReportCellData.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 LabKey Corporation + * Copyright (c) 2008-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/report/SpecimenReportExcelWriter.java b/specimen/src/org/labkey/specimen/report/SpecimenReportExcelWriter.java index a37e0887..27710546 100644 --- a/specimen/src/org/labkey/specimen/report/SpecimenReportExcelWriter.java +++ b/specimen/src/org/labkey/specimen/report/SpecimenReportExcelWriter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 LabKey Corporation + * Copyright (c) 2008-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/report/SpecimenReportManager.java b/specimen/src/org/labkey/specimen/report/SpecimenReportManager.java index 166d826d..3e127c7d 100644 --- a/specimen/src/org/labkey/specimen/report/SpecimenReportManager.java +++ b/specimen/src/org/labkey/specimen/report/SpecimenReportManager.java @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2021-2026 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.specimen.report; import org.apache.commons.beanutils.PropertyUtils; diff --git a/specimen/src/org/labkey/specimen/report/SpecimenReportTitle.java b/specimen/src/org/labkey/specimen/report/SpecimenReportTitle.java index 435a163a..3809f05a 100644 --- a/specimen/src/org/labkey/specimen/report/SpecimenReportTitle.java +++ b/specimen/src/org/labkey/specimen/report/SpecimenReportTitle.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 LabKey Corporation + * Copyright (c) 2011-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/report/SpecimenTypeBeanProperty.java b/specimen/src/org/labkey/specimen/report/SpecimenTypeBeanProperty.java index e48d48a4..5a4e01a8 100644 --- a/specimen/src/org/labkey/specimen/report/SpecimenTypeBeanProperty.java +++ b/specimen/src/org/labkey/specimen/report/SpecimenTypeBeanProperty.java @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2021-2026 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.specimen.report; import org.labkey.api.query.FieldKey; diff --git a/specimen/src/org/labkey/specimen/report/SpecimenTypeLevel.java b/specimen/src/org/labkey/specimen/report/SpecimenTypeLevel.java index bdf113c3..cfcb7845 100644 --- a/specimen/src/org/labkey/specimen/report/SpecimenTypeLevel.java +++ b/specimen/src/org/labkey/specimen/report/SpecimenTypeLevel.java @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2021-2026 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.specimen.report; import org.labkey.api.query.FieldKey; diff --git a/specimen/src/org/labkey/specimen/report/SpecimenTypeVisitReport.java b/specimen/src/org/labkey/specimen/report/SpecimenTypeVisitReport.java index 61d5899c..11127337 100644 --- a/specimen/src/org/labkey/specimen/report/SpecimenTypeVisitReport.java +++ b/specimen/src/org/labkey/specimen/report/SpecimenTypeVisitReport.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 LabKey Corporation + * Copyright (c) 2008-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/report/SpecimenVisitReport.java b/specimen/src/org/labkey/specimen/report/SpecimenVisitReport.java index eee91eb4..52530500 100644 --- a/specimen/src/org/labkey/specimen/report/SpecimenVisitReport.java +++ b/specimen/src/org/labkey/specimen/report/SpecimenVisitReport.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 LabKey Corporation + * Copyright (c) 2008-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/report/SpecimenVisitReportAction.java b/specimen/src/org/labkey/specimen/report/SpecimenVisitReportAction.java index c3a4e99c..5e0ca708 100644 --- a/specimen/src/org/labkey/specimen/report/SpecimenVisitReportAction.java +++ b/specimen/src/org/labkey/specimen/report/SpecimenVisitReportAction.java @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2021-2026 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.specimen.report; import org.labkey.api.action.SimpleViewAction; diff --git a/specimen/src/org/labkey/specimen/report/SpecimenVisitReportParameters.java b/specimen/src/org/labkey/specimen/report/SpecimenVisitReportParameters.java index 882a5e07..586d415b 100644 --- a/specimen/src/org/labkey/specimen/report/SpecimenVisitReportParameters.java +++ b/specimen/src/org/labkey/specimen/report/SpecimenVisitReportParameters.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 LabKey Corporation + * Copyright (c) 2008-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/report/SummaryByVisitParticipant.java b/specimen/src/org/labkey/specimen/report/SummaryByVisitParticipant.java index 61d07def..209fc33d 100644 --- a/specimen/src/org/labkey/specimen/report/SummaryByVisitParticipant.java +++ b/specimen/src/org/labkey/specimen/report/SummaryByVisitParticipant.java @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2021-2026 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.specimen.report; public class SummaryByVisitParticipant extends SpecimenCountSummary diff --git a/specimen/src/org/labkey/specimen/report/SummaryByVisitType.java b/specimen/src/org/labkey/specimen/report/SummaryByVisitType.java index 7c0be688..03ae5641 100644 --- a/specimen/src/org/labkey/specimen/report/SummaryByVisitType.java +++ b/specimen/src/org/labkey/specimen/report/SummaryByVisitType.java @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2021-2026 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.specimen.report; import java.util.Set; diff --git a/specimen/src/org/labkey/specimen/report/participant/ParticipantSiteReport.java b/specimen/src/org/labkey/specimen/report/participant/ParticipantSiteReport.java index 670d35d7..46f6b8eb 100644 --- a/specimen/src/org/labkey/specimen/report/participant/ParticipantSiteReport.java +++ b/specimen/src/org/labkey/specimen/report/participant/ParticipantSiteReport.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 LabKey Corporation + * Copyright (c) 2008-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/report/participant/ParticipantSiteReportFactory.java b/specimen/src/org/labkey/specimen/report/participant/ParticipantSiteReportFactory.java index a5c03ef1..0402fbf3 100644 --- a/specimen/src/org/labkey/specimen/report/participant/ParticipantSiteReportFactory.java +++ b/specimen/src/org/labkey/specimen/report/participant/ParticipantSiteReportFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 LabKey Corporation + * Copyright (c) 2008-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/report/participant/ParticipantSummaryReportFactory.java b/specimen/src/org/labkey/specimen/report/participant/ParticipantSummaryReportFactory.java index e4c3e31a..056fea6c 100644 --- a/specimen/src/org/labkey/specimen/report/participant/ParticipantSummaryReportFactory.java +++ b/specimen/src/org/labkey/specimen/report/participant/ParticipantSummaryReportFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 LabKey Corporation + * Copyright (c) 2008-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/report/participant/ParticipantTypeReportFactory.java b/specimen/src/org/labkey/specimen/report/participant/ParticipantTypeReportFactory.java index 3fdec4f8..9b4bd0a4 100644 --- a/specimen/src/org/labkey/specimen/report/participant/ParticipantTypeReportFactory.java +++ b/specimen/src/org/labkey/specimen/report/participant/ParticipantTypeReportFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 LabKey Corporation + * Copyright (c) 2008-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/report/participant/ParticipantVisitReport.java b/specimen/src/org/labkey/specimen/report/participant/ParticipantVisitReport.java index 51691c6b..6a9cabe6 100644 --- a/specimen/src/org/labkey/specimen/report/participant/ParticipantVisitReport.java +++ b/specimen/src/org/labkey/specimen/report/participant/ParticipantVisitReport.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 LabKey Corporation + * Copyright (c) 2008-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/report/request/BaseRequestReportFactory.java b/specimen/src/org/labkey/specimen/report/request/BaseRequestReportFactory.java index 49285411..96ae7815 100644 --- a/specimen/src/org/labkey/specimen/report/request/BaseRequestReportFactory.java +++ b/specimen/src/org/labkey/specimen/report/request/BaseRequestReportFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 LabKey Corporation + * Copyright (c) 2009-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/report/request/RequestEnrollmentLocationReport.java b/specimen/src/org/labkey/specimen/report/request/RequestEnrollmentLocationReport.java index a3155c52..d1f11225 100644 --- a/specimen/src/org/labkey/specimen/report/request/RequestEnrollmentLocationReport.java +++ b/specimen/src/org/labkey/specimen/report/request/RequestEnrollmentLocationReport.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 LabKey Corporation + * Copyright (c) 2008-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/report/request/RequestEnrollmentSiteReportFactory.java b/specimen/src/org/labkey/specimen/report/request/RequestEnrollmentSiteReportFactory.java index fe386095..691c150d 100644 --- a/specimen/src/org/labkey/specimen/report/request/RequestEnrollmentSiteReportFactory.java +++ b/specimen/src/org/labkey/specimen/report/request/RequestEnrollmentSiteReportFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 LabKey Corporation + * Copyright (c) 2008-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/report/request/RequestLocationReport.java b/specimen/src/org/labkey/specimen/report/request/RequestLocationReport.java index 7b7c55f5..520dc872 100644 --- a/specimen/src/org/labkey/specimen/report/request/RequestLocationReport.java +++ b/specimen/src/org/labkey/specimen/report/request/RequestLocationReport.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 LabKey Corporation + * Copyright (c) 2008-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/report/request/RequestLocationReportFactory.java b/specimen/src/org/labkey/specimen/report/request/RequestLocationReportFactory.java index 9110aa7b..7f5390fd 100644 --- a/specimen/src/org/labkey/specimen/report/request/RequestLocationReportFactory.java +++ b/specimen/src/org/labkey/specimen/report/request/RequestLocationReportFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 LabKey Corporation + * Copyright (c) 2008-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/report/request/RequestParticipantReport.java b/specimen/src/org/labkey/specimen/report/request/RequestParticipantReport.java index 5cd207a1..e5d76b3e 100644 --- a/specimen/src/org/labkey/specimen/report/request/RequestParticipantReport.java +++ b/specimen/src/org/labkey/specimen/report/request/RequestParticipantReport.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 LabKey Corporation + * Copyright (c) 2008-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/report/request/RequestParticipantReportFactory.java b/specimen/src/org/labkey/specimen/report/request/RequestParticipantReportFactory.java index 59a1aa7d..e1a70e63 100644 --- a/specimen/src/org/labkey/specimen/report/request/RequestParticipantReportFactory.java +++ b/specimen/src/org/labkey/specimen/report/request/RequestParticipantReportFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 LabKey Corporation + * Copyright (c) 2008-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/report/request/RequestReport.java b/specimen/src/org/labkey/specimen/report/request/RequestReport.java index 59c895b8..2ce435db 100644 --- a/specimen/src/org/labkey/specimen/report/request/RequestReport.java +++ b/specimen/src/org/labkey/specimen/report/request/RequestReport.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 LabKey Corporation + * Copyright (c) 2008-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/report/request/RequestReportFactory.java b/specimen/src/org/labkey/specimen/report/request/RequestReportFactory.java index 99e8b29f..0c4802d8 100644 --- a/specimen/src/org/labkey/specimen/report/request/RequestReportFactory.java +++ b/specimen/src/org/labkey/specimen/report/request/RequestReportFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 LabKey Corporation + * Copyright (c) 2008-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/report/specimentype/TypeCohortReport.java b/specimen/src/org/labkey/specimen/report/specimentype/TypeCohortReport.java index 5ea79986..350fc5c0 100644 --- a/specimen/src/org/labkey/specimen/report/specimentype/TypeCohortReport.java +++ b/specimen/src/org/labkey/specimen/report/specimentype/TypeCohortReport.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 LabKey Corporation + * Copyright (c) 2008-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/report/specimentype/TypeCohortReportFactory.java b/specimen/src/org/labkey/specimen/report/specimentype/TypeCohortReportFactory.java index 2d33b91c..c86fd0d7 100644 --- a/specimen/src/org/labkey/specimen/report/specimentype/TypeCohortReportFactory.java +++ b/specimen/src/org/labkey/specimen/report/specimentype/TypeCohortReportFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 LabKey Corporation + * Copyright (c) 2008-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/report/specimentype/TypeParticipantReportFactory.java b/specimen/src/org/labkey/specimen/report/specimentype/TypeParticipantReportFactory.java index 72f8e31b..d1cca1c1 100644 --- a/specimen/src/org/labkey/specimen/report/specimentype/TypeParticipantReportFactory.java +++ b/specimen/src/org/labkey/specimen/report/specimentype/TypeParticipantReportFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 LabKey Corporation + * Copyright (c) 2008-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/report/specimentype/TypeReportFactory.java b/specimen/src/org/labkey/specimen/report/specimentype/TypeReportFactory.java index ac1d53bc..d6f2a5f9 100644 --- a/specimen/src/org/labkey/specimen/report/specimentype/TypeReportFactory.java +++ b/specimen/src/org/labkey/specimen/report/specimentype/TypeReportFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 LabKey Corporation + * Copyright (c) 2008-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/report/specimentype/TypeSummaryReportFactory.java b/specimen/src/org/labkey/specimen/report/specimentype/TypeSummaryReportFactory.java index 96dd61aa..7b37bbea 100644 --- a/specimen/src/org/labkey/specimen/report/specimentype/TypeSummaryReportFactory.java +++ b/specimen/src/org/labkey/specimen/report/specimentype/TypeSummaryReportFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 LabKey Corporation + * Copyright (c) 2008-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/requirements/DefaultActor.java b/specimen/src/org/labkey/specimen/requirements/DefaultActor.java index 62be745a..96e0f55f 100644 --- a/specimen/src/org/labkey/specimen/requirements/DefaultActor.java +++ b/specimen/src/org/labkey/specimen/requirements/DefaultActor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008-2019 LabKey Corporation + * Copyright (c) 2008-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/requirements/DefaultRequirement.java b/specimen/src/org/labkey/specimen/requirements/DefaultRequirement.java index 9117ea9d..ef2d1c37 100644 --- a/specimen/src/org/labkey/specimen/requirements/DefaultRequirement.java +++ b/specimen/src/org/labkey/specimen/requirements/DefaultRequirement.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008-2019 LabKey Corporation + * Copyright (c) 2008-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/requirements/DefaultRequirementProvider.java b/specimen/src/org/labkey/specimen/requirements/DefaultRequirementProvider.java index 803e98fd..c3c8e17b 100644 --- a/specimen/src/org/labkey/specimen/requirements/DefaultRequirementProvider.java +++ b/specimen/src/org/labkey/specimen/requirements/DefaultRequirementProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008-2019 LabKey Corporation + * Copyright (c) 2008-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/requirements/Requirement.java b/specimen/src/org/labkey/specimen/requirements/Requirement.java index d9d8ba15..975a32c9 100644 --- a/specimen/src/org/labkey/specimen/requirements/Requirement.java +++ b/specimen/src/org/labkey/specimen/requirements/Requirement.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008-2019 LabKey Corporation + * Copyright (c) 2008-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/requirements/RequirementActor.java b/specimen/src/org/labkey/specimen/requirements/RequirementActor.java index a6a1e481..144a89c7 100644 --- a/specimen/src/org/labkey/specimen/requirements/RequirementActor.java +++ b/specimen/src/org/labkey/specimen/requirements/RequirementActor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008-2019 LabKey Corporation + * Copyright (c) 2008-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/requirements/RequirementOwner.java b/specimen/src/org/labkey/specimen/requirements/RequirementOwner.java index c471a785..96543d54 100644 --- a/specimen/src/org/labkey/specimen/requirements/RequirementOwner.java +++ b/specimen/src/org/labkey/specimen/requirements/RequirementOwner.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008-2019 LabKey Corporation + * Copyright (c) 2008-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/requirements/RequirementProvider.java b/specimen/src/org/labkey/specimen/requirements/RequirementProvider.java index d8c90276..0c57fd81 100644 --- a/specimen/src/org/labkey/specimen/requirements/RequirementProvider.java +++ b/specimen/src/org/labkey/specimen/requirements/RequirementProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008-2019 LabKey Corporation + * Copyright (c) 2008-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/requirements/RequirementType.java b/specimen/src/org/labkey/specimen/requirements/RequirementType.java index 646bb8ec..c3b72a93 100644 --- a/specimen/src/org/labkey/specimen/requirements/RequirementType.java +++ b/specimen/src/org/labkey/specimen/requirements/RequirementType.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008-2019 LabKey Corporation + * Copyright (c) 2008-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/requirements/SpecimenRequest.java b/specimen/src/org/labkey/specimen/requirements/SpecimenRequest.java index 4d73a45e..ca632135 100644 --- a/specimen/src/org/labkey/specimen/requirements/SpecimenRequest.java +++ b/specimen/src/org/labkey/specimen/requirements/SpecimenRequest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 LabKey Corporation + * Copyright (c) 2008-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/requirements/SpecimenRequestRequirement.java b/specimen/src/org/labkey/specimen/requirements/SpecimenRequestRequirement.java index edd50de3..34a76260 100644 --- a/specimen/src/org/labkey/specimen/requirements/SpecimenRequestRequirement.java +++ b/specimen/src/org/labkey/specimen/requirements/SpecimenRequestRequirement.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 LabKey Corporation + * Copyright (c) 2008-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/requirements/SpecimenRequestRequirementProvider.java b/specimen/src/org/labkey/specimen/requirements/SpecimenRequestRequirementProvider.java index 4af4b5ab..9290f8d9 100644 --- a/specimen/src/org/labkey/specimen/requirements/SpecimenRequestRequirementProvider.java +++ b/specimen/src/org/labkey/specimen/requirements/SpecimenRequestRequirementProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008-2019 LabKey Corporation + * Copyright (c) 2008-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/requirements/SpecimenRequestRequirementType.java b/specimen/src/org/labkey/specimen/requirements/SpecimenRequestRequirementType.java index 3595d1e4..6186160d 100644 --- a/specimen/src/org/labkey/specimen/requirements/SpecimenRequestRequirementType.java +++ b/specimen/src/org/labkey/specimen/requirements/SpecimenRequestRequirementType.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008-2019 LabKey Corporation + * Copyright (c) 2008-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/security/permissions/LockSpecimensPermission.java b/specimen/src/org/labkey/specimen/security/permissions/LockSpecimensPermission.java index e94d0697..d54fd447 100644 --- a/specimen/src/org/labkey/specimen/security/permissions/LockSpecimensPermission.java +++ b/specimen/src/org/labkey/specimen/security/permissions/LockSpecimensPermission.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2012 LabKey Corporation + * Copyright (c) 2009-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/security/permissions/ManageDisplaySettingsPermission.java b/specimen/src/org/labkey/specimen/security/permissions/ManageDisplaySettingsPermission.java index 77415037..a3b77c19 100644 --- a/specimen/src/org/labkey/specimen/security/permissions/ManageDisplaySettingsPermission.java +++ b/specimen/src/org/labkey/specimen/security/permissions/ManageDisplaySettingsPermission.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2012 LabKey Corporation + * Copyright (c) 2009-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/security/permissions/ManageNewRequestFormPermission.java b/specimen/src/org/labkey/specimen/security/permissions/ManageNewRequestFormPermission.java index 1523cc94..360f3ad1 100644 --- a/specimen/src/org/labkey/specimen/security/permissions/ManageNewRequestFormPermission.java +++ b/specimen/src/org/labkey/specimen/security/permissions/ManageNewRequestFormPermission.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2017 LabKey Corporation + * Copyright (c) 2009-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/security/permissions/ManageNotificationsPermission.java b/specimen/src/org/labkey/specimen/security/permissions/ManageNotificationsPermission.java index 095df7f7..952500e2 100644 --- a/specimen/src/org/labkey/specimen/security/permissions/ManageNotificationsPermission.java +++ b/specimen/src/org/labkey/specimen/security/permissions/ManageNotificationsPermission.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2012 LabKey Corporation + * Copyright (c) 2009-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/security/permissions/ManageRequestRequirementsPermission.java b/specimen/src/org/labkey/specimen/security/permissions/ManageRequestRequirementsPermission.java index 4fcdffd1..9c0805db 100644 --- a/specimen/src/org/labkey/specimen/security/permissions/ManageRequestRequirementsPermission.java +++ b/specimen/src/org/labkey/specimen/security/permissions/ManageRequestRequirementsPermission.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2012 LabKey Corporation + * Copyright (c) 2009-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/security/permissions/ManageRequestStatusesPermission.java b/specimen/src/org/labkey/specimen/security/permissions/ManageRequestStatusesPermission.java index a4dc555d..0699ea5c 100644 --- a/specimen/src/org/labkey/specimen/security/permissions/ManageRequestStatusesPermission.java +++ b/specimen/src/org/labkey/specimen/security/permissions/ManageRequestStatusesPermission.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2012 LabKey Corporation + * Copyright (c) 2009-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/security/permissions/ManageRequestsPermission.java b/specimen/src/org/labkey/specimen/security/permissions/ManageRequestsPermission.java index 667766b3..bf85c5f7 100644 --- a/specimen/src/org/labkey/specimen/security/permissions/ManageRequestsPermission.java +++ b/specimen/src/org/labkey/specimen/security/permissions/ManageRequestsPermission.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2011 LabKey Corporation + * Copyright (c) 2009-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/security/permissions/ManageSpecimenActorsPermission.java b/specimen/src/org/labkey/specimen/security/permissions/ManageSpecimenActorsPermission.java index a69196b3..6a997f21 100644 --- a/specimen/src/org/labkey/specimen/security/permissions/ManageSpecimenActorsPermission.java +++ b/specimen/src/org/labkey/specimen/security/permissions/ManageSpecimenActorsPermission.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2012 LabKey Corporation + * Copyright (c) 2009-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/security/permissions/SetSpecimenCommentsPermission.java b/specimen/src/org/labkey/specimen/security/permissions/SetSpecimenCommentsPermission.java index 29b16bb3..2acb1515 100644 --- a/specimen/src/org/labkey/specimen/security/permissions/SetSpecimenCommentsPermission.java +++ b/specimen/src/org/labkey/specimen/security/permissions/SetSpecimenCommentsPermission.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2012 LabKey Corporation + * Copyright (c) 2009-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/security/roles/AbstractSpecimenRole.java b/specimen/src/org/labkey/specimen/security/roles/AbstractSpecimenRole.java index 97832c3c..dd8602c6 100644 --- a/specimen/src/org/labkey/specimen/security/roles/AbstractSpecimenRole.java +++ b/specimen/src/org/labkey/specimen/security/roles/AbstractSpecimenRole.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011-2018 LabKey Corporation + * Copyright (c) 2011-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/security/roles/SpecimenCoordinatorRole.java b/specimen/src/org/labkey/specimen/security/roles/SpecimenCoordinatorRole.java index ead2d1f1..1c2d4850 100644 --- a/specimen/src/org/labkey/specimen/security/roles/SpecimenCoordinatorRole.java +++ b/specimen/src/org/labkey/specimen/security/roles/SpecimenCoordinatorRole.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2013 LabKey Corporation + * Copyright (c) 2021-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/security/roles/SpecimenRequesterRole.java b/specimen/src/org/labkey/specimen/security/roles/SpecimenRequesterRole.java index 5b19d28b..c469da2d 100644 --- a/specimen/src/org/labkey/specimen/security/roles/SpecimenRequesterRole.java +++ b/specimen/src/org/labkey/specimen/security/roles/SpecimenRequesterRole.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2012 LabKey Corporation + * Copyright (c) 2009-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/settings/DisplaySettings.java b/specimen/src/org/labkey/specimen/settings/DisplaySettings.java index dbbfbb26..32841b40 100644 --- a/specimen/src/org/labkey/specimen/settings/DisplaySettings.java +++ b/specimen/src/org/labkey/specimen/settings/DisplaySettings.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 LabKey Corporation + * Copyright (c) 2009-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/settings/RepositorySettings.java b/specimen/src/org/labkey/specimen/settings/RepositorySettings.java index c0da590c..f7da8f95 100644 --- a/specimen/src/org/labkey/specimen/settings/RepositorySettings.java +++ b/specimen/src/org/labkey/specimen/settings/RepositorySettings.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 LabKey Corporation + * Copyright (c) 2009-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/settings/RequestNotificationSettings.java b/specimen/src/org/labkey/specimen/settings/RequestNotificationSettings.java index a3db5300..97718896 100644 --- a/specimen/src/org/labkey/specimen/settings/RequestNotificationSettings.java +++ b/specimen/src/org/labkey/specimen/settings/RequestNotificationSettings.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 LabKey Corporation + * Copyright (c) 2009-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/settings/SettingsManager.java b/specimen/src/org/labkey/specimen/settings/SettingsManager.java index 44ade1bd..5cd0dd15 100644 --- a/specimen/src/org/labkey/specimen/settings/SettingsManager.java +++ b/specimen/src/org/labkey/specimen/settings/SettingsManager.java @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2024-2026 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.specimen.settings; import org.labkey.api.action.SpringActionController; diff --git a/specimen/src/org/labkey/specimen/settings/StatusSettings.java b/specimen/src/org/labkey/specimen/settings/StatusSettings.java index f2341d01..291da06d 100644 --- a/specimen/src/org/labkey/specimen/settings/StatusSettings.java +++ b/specimen/src/org/labkey/specimen/settings/StatusSettings.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 LabKey Corporation + * Copyright (c) 2009-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/view/ManageSpecimenView.java b/specimen/src/org/labkey/specimen/view/ManageSpecimenView.java index 2be73ebb..d36a747c 100644 --- a/specimen/src/org/labkey/specimen/view/ManageSpecimenView.java +++ b/specimen/src/org/labkey/specimen/view/ManageSpecimenView.java @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2021-2026 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.specimen.view; import org.labkey.api.view.JspView; diff --git a/specimen/src/org/labkey/specimen/view/NotificationBean.java b/specimen/src/org/labkey/specimen/view/NotificationBean.java index cf156ce4..06e00700 100644 --- a/specimen/src/org/labkey/specimen/view/NotificationBean.java +++ b/specimen/src/org/labkey/specimen/view/NotificationBean.java @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2021-2026 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.specimen.view; import org.jetbrains.annotations.NotNull; diff --git a/specimen/src/org/labkey/specimen/view/SpecimenReportWebPartFactory.java b/specimen/src/org/labkey/specimen/view/SpecimenReportWebPartFactory.java index 31521b94..0754a8a9 100644 --- a/specimen/src/org/labkey/specimen/view/SpecimenReportWebPartFactory.java +++ b/specimen/src/org/labkey/specimen/view/SpecimenReportWebPartFactory.java @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2021-2026 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.specimen.view; import org.jetbrains.annotations.NotNull; diff --git a/specimen/src/org/labkey/specimen/view/SpecimenRequestNotificationEmailTemplate.java b/specimen/src/org/labkey/specimen/view/SpecimenRequestNotificationEmailTemplate.java index 1c91886c..70d1edc2 100644 --- a/specimen/src/org/labkey/specimen/view/SpecimenRequestNotificationEmailTemplate.java +++ b/specimen/src/org/labkey/specimen/view/SpecimenRequestNotificationEmailTemplate.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 LabKey Corporation + * Copyright (c) 2013-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/view/SpecimenSearchWebPart.java b/specimen/src/org/labkey/specimen/view/SpecimenSearchWebPart.java index cb7121e2..4a95964f 100644 --- a/specimen/src/org/labkey/specimen/view/SpecimenSearchWebPart.java +++ b/specimen/src/org/labkey/specimen/view/SpecimenSearchWebPart.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 LabKey Corporation + * Copyright (c) 2010-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/view/SpecimenSearchWebPartFactory.java b/specimen/src/org/labkey/specimen/view/SpecimenSearchWebPartFactory.java index 977a814d..324c8506 100644 --- a/specimen/src/org/labkey/specimen/view/SpecimenSearchWebPartFactory.java +++ b/specimen/src/org/labkey/specimen/view/SpecimenSearchWebPartFactory.java @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2021-2026 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.specimen.view; import org.jetbrains.annotations.NotNull; diff --git a/specimen/src/org/labkey/specimen/view/SpecimenToolsWebPartFactory.java b/specimen/src/org/labkey/specimen/view/SpecimenToolsWebPartFactory.java index 84c655f6..bed997ca 100644 --- a/specimen/src/org/labkey/specimen/view/SpecimenToolsWebPartFactory.java +++ b/specimen/src/org/labkey/specimen/view/SpecimenToolsWebPartFactory.java @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2021-2026 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.specimen.view; import org.labkey.api.specimen.security.permissions.RequestSpecimensPermission; diff --git a/specimen/src/org/labkey/specimen/view/SpecimenWebPart.java b/specimen/src/org/labkey/specimen/view/SpecimenWebPart.java index 74eda80d..100e9d75 100644 --- a/specimen/src/org/labkey/specimen/view/SpecimenWebPart.java +++ b/specimen/src/org/labkey/specimen/view/SpecimenWebPart.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 LabKey Corporation + * Copyright (c) 2014-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/view/SpecimenWebPartFactory.java b/specimen/src/org/labkey/specimen/view/SpecimenWebPartFactory.java index 14b02ded..8863d0e1 100644 --- a/specimen/src/org/labkey/specimen/view/SpecimenWebPartFactory.java +++ b/specimen/src/org/labkey/specimen/view/SpecimenWebPartFactory.java @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2021-2026 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.specimen.view; import org.jetbrains.annotations.NotNull; diff --git a/specimen/src/org/labkey/specimen/view/autoReportList.jsp b/specimen/src/org/labkey/specimen/view/autoReportList.jsp index 174d1619..21b0c536 100644 --- a/specimen/src/org/labkey/specimen/view/autoReportList.jsp +++ b/specimen/src/org/labkey/specimen/view/autoReportList.jsp @@ -1,6 +1,6 @@ <% /* - * Copyright (c) 2014-2019 LabKey Corporation + * Copyright (c) 2008-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/view/chooseImporter.jsp b/specimen/src/org/labkey/specimen/view/chooseImporter.jsp index 0fc794dd..5798fe92 100644 --- a/specimen/src/org/labkey/specimen/view/chooseImporter.jsp +++ b/specimen/src/org/labkey/specimen/view/chooseImporter.jsp @@ -1,7 +1,18 @@ <% /* - * Copyright (c) 2013-2016 LabKey Corporation. All rights reserved. No portion of this work may be reproduced in - * any form or by any electronic or mechanical means without written permission from LabKey Corporation. + * Copyright (c) 2020-2026 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="com.google.common.collect.Iterables" %> diff --git a/specimen/src/org/labkey/specimen/view/configRequestabilityRules.jsp b/specimen/src/org/labkey/specimen/view/configRequestabilityRules.jsp index f162164e..0c847291 100644 --- a/specimen/src/org/labkey/specimen/view/configRequestabilityRules.jsp +++ b/specimen/src/org/labkey/specimen/view/configRequestabilityRules.jsp @@ -1,6 +1,6 @@ <% /* - * Copyright (c) 2014-2019 LabKey Corporation + * Copyright (c) 2010-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/view/configurationRequired.jsp b/specimen/src/org/labkey/specimen/view/configurationRequired.jsp index e95e0ba9..19aaf5f7 100644 --- a/specimen/src/org/labkey/specimen/view/configurationRequired.jsp +++ b/specimen/src/org/labkey/specimen/view/configurationRequired.jsp @@ -1,6 +1,6 @@ <% /* - * Copyright (c) 2014-2019 LabKey Corporation + * Copyright (c) 2008-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/view/configureQueryImport.jsp b/specimen/src/org/labkey/specimen/view/configureQueryImport.jsp index f62ceaea..f3f60e80 100644 --- a/specimen/src/org/labkey/specimen/view/configureQueryImport.jsp +++ b/specimen/src/org/labkey/specimen/view/configureQueryImport.jsp @@ -1,7 +1,18 @@ <% /* - * Copyright (c) 2013-2016 LabKey Corporation. All rights reserved. No portion of this work may be reproduced in - * any form or by any electronic or mechanical means without written permission from LabKey Corporation. + * Copyright (c) 2026 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" %> diff --git a/specimen/src/org/labkey/specimen/view/customizeSpecimenReportWebPart.jsp b/specimen/src/org/labkey/specimen/view/customizeSpecimenReportWebPart.jsp index a5a52cd3..b76ecc0f 100644 --- a/specimen/src/org/labkey/specimen/view/customizeSpecimenReportWebPart.jsp +++ b/specimen/src/org/labkey/specimen/view/customizeSpecimenReportWebPart.jsp @@ -1,6 +1,6 @@ <% /* - * Copyright (c) 2014 LabKey Corporation + * Copyright (c) 2014-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/view/groupMembers.jsp b/specimen/src/org/labkey/specimen/view/groupMembers.jsp index 6799bf3c..4b3d044b 100644 --- a/specimen/src/org/labkey/specimen/view/groupMembers.jsp +++ b/specimen/src/org/labkey/specimen/view/groupMembers.jsp @@ -1,6 +1,6 @@ <% /* - * Copyright (c) 2014-2019 LabKey Corporation + * Copyright (c) 2008-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/view/importSpecimens.jsp b/specimen/src/org/labkey/specimen/view/importSpecimens.jsp index 07bd622e..2773cbc5 100644 --- a/specimen/src/org/labkey/specimen/view/importSpecimens.jsp +++ b/specimen/src/org/labkey/specimen/view/importSpecimens.jsp @@ -1,6 +1,6 @@ <% /* - * Copyright (c) 2014-2019 LabKey Corporation + * Copyright (c) 2008-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/view/labSpecimenLists.jsp b/specimen/src/org/labkey/specimen/view/labSpecimenLists.jsp index ebee55b5..a592622c 100644 --- a/specimen/src/org/labkey/specimen/view/labSpecimenLists.jsp +++ b/specimen/src/org/labkey/specimen/view/labSpecimenLists.jsp @@ -1,6 +1,6 @@ <% /* - * Copyright (c) 2014-2019 LabKey Corporation + * Copyright (c) 2008-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/view/manageActorOrder.jsp b/specimen/src/org/labkey/specimen/view/manageActorOrder.jsp index 56fdacac..7e006c8d 100644 --- a/specimen/src/org/labkey/specimen/view/manageActorOrder.jsp +++ b/specimen/src/org/labkey/specimen/view/manageActorOrder.jsp @@ -1,6 +1,6 @@ <% /* - * Copyright (c) 2014-2019 LabKey Corporation + * Copyright (c) 2008-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/view/manageActors.jsp b/specimen/src/org/labkey/specimen/view/manageActors.jsp index 2574d5c3..d9ff8285 100644 --- a/specimen/src/org/labkey/specimen/view/manageActors.jsp +++ b/specimen/src/org/labkey/specimen/view/manageActors.jsp @@ -1,6 +1,6 @@ <% /* - * Copyright (c) 2014-2019 LabKey Corporation + * Copyright (c) 2008-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/view/manageComments.jsp b/specimen/src/org/labkey/specimen/view/manageComments.jsp index 368f4149..e449f2ee 100644 --- a/specimen/src/org/labkey/specimen/view/manageComments.jsp +++ b/specimen/src/org/labkey/specimen/view/manageComments.jsp @@ -1,6 +1,6 @@ <% /* - * Copyright (c) 2009-2017 LabKey Corporation + * Copyright (c) 2009-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/view/manageDefaultReqs.jsp b/specimen/src/org/labkey/specimen/view/manageDefaultReqs.jsp index 0d691e65..7dba116a 100644 --- a/specimen/src/org/labkey/specimen/view/manageDefaultReqs.jsp +++ b/specimen/src/org/labkey/specimen/view/manageDefaultReqs.jsp @@ -1,6 +1,6 @@ <% /* - * Copyright (c) 2014-2019 LabKey Corporation + * Copyright (c) 2008-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/view/manageDisplay.jsp b/specimen/src/org/labkey/specimen/view/manageDisplay.jsp index 5cefbe04..997925a2 100644 --- a/specimen/src/org/labkey/specimen/view/manageDisplay.jsp +++ b/specimen/src/org/labkey/specimen/view/manageDisplay.jsp @@ -1,6 +1,6 @@ <% /* - * Copyright (c) 2014-2019 LabKey Corporation + * Copyright (c) 2008-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/view/manageLocationTypes.jsp b/specimen/src/org/labkey/specimen/view/manageLocationTypes.jsp index 5844f92e..fcecd6fc 100644 --- a/specimen/src/org/labkey/specimen/view/manageLocationTypes.jsp +++ b/specimen/src/org/labkey/specimen/view/manageLocationTypes.jsp @@ -1,6 +1,6 @@ <% /* - * Copyright (c) 2012-2017 LabKey Corporation + * Copyright (c) 2012-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/view/manageNotifications.jsp b/specimen/src/org/labkey/specimen/view/manageNotifications.jsp index 416f70e9..3ab1ec35 100644 --- a/specimen/src/org/labkey/specimen/view/manageNotifications.jsp +++ b/specimen/src/org/labkey/specimen/view/manageNotifications.jsp @@ -1,6 +1,6 @@ <% /* - * Copyright (c) 2014-2019 LabKey Corporation + * Copyright (c) 2008-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/view/manageRepositorySettings.jsp b/specimen/src/org/labkey/specimen/view/manageRepositorySettings.jsp index e6b7a6bd..5e52f5e7 100644 --- a/specimen/src/org/labkey/specimen/view/manageRepositorySettings.jsp +++ b/specimen/src/org/labkey/specimen/view/manageRepositorySettings.jsp @@ -1,6 +1,6 @@ <% /* - * Copyright (c) 2014-2019 LabKey Corporation + * Copyright (c) 2008-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/view/manageRequest.jsp b/specimen/src/org/labkey/specimen/view/manageRequest.jsp index 0403819d..96df8b7e 100644 --- a/specimen/src/org/labkey/specimen/view/manageRequest.jsp +++ b/specimen/src/org/labkey/specimen/view/manageRequest.jsp @@ -1,6 +1,6 @@ <% /* - * Copyright (c) 2014-2019 LabKey Corporation + * Copyright (c) 2008-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/view/manageRequestInputs.jsp b/specimen/src/org/labkey/specimen/view/manageRequestInputs.jsp index 8d20c6be..163b1fc4 100644 --- a/specimen/src/org/labkey/specimen/view/manageRequestInputs.jsp +++ b/specimen/src/org/labkey/specimen/view/manageRequestInputs.jsp @@ -1,6 +1,6 @@ <% /* - * Copyright (c) 2014-2019 LabKey Corporation + * Copyright (c) 2008-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/view/manageRequestStatus.jsp b/specimen/src/org/labkey/specimen/view/manageRequestStatus.jsp index 42784ebb..90040173 100644 --- a/specimen/src/org/labkey/specimen/view/manageRequestStatus.jsp +++ b/specimen/src/org/labkey/specimen/view/manageRequestStatus.jsp @@ -1,6 +1,6 @@ <% /* - * Copyright (c) 2014-2019 LabKey Corporation + * Copyright (c) 2008-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/view/manageRequirement.jsp b/specimen/src/org/labkey/specimen/view/manageRequirement.jsp index 68109262..f3363a2a 100644 --- a/specimen/src/org/labkey/specimen/view/manageRequirement.jsp +++ b/specimen/src/org/labkey/specimen/view/manageRequirement.jsp @@ -1,6 +1,6 @@ <% /* - * Copyright (c) 2014-2019 LabKey Corporation + * Copyright (c) 2008-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/view/manageSpecimenWebPart.jsp b/specimen/src/org/labkey/specimen/view/manageSpecimenWebPart.jsp index c8d5d51a..e9937bd1 100644 --- a/specimen/src/org/labkey/specimen/view/manageSpecimenWebPart.jsp +++ b/specimen/src/org/labkey/specimen/view/manageSpecimenWebPart.jsp @@ -1,6 +1,6 @@ <% /* - * Copyright (c) 2014-2019 LabKey Corporation + * Copyright (c) 2013-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/view/manageSpecimens.jsp b/specimen/src/org/labkey/specimen/view/manageSpecimens.jsp index 3f221e66..d84696c2 100644 --- a/specimen/src/org/labkey/specimen/view/manageSpecimens.jsp +++ b/specimen/src/org/labkey/specimen/view/manageSpecimens.jsp @@ -1,6 +1,6 @@ <% /* - * Copyright (c) 2008-2019 LabKey Corporation + * Copyright (c) 2008-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/view/manageStatusOrder.jsp b/specimen/src/org/labkey/specimen/view/manageStatusOrder.jsp index b971827f..8be53938 100644 --- a/specimen/src/org/labkey/specimen/view/manageStatusOrder.jsp +++ b/specimen/src/org/labkey/specimen/view/manageStatusOrder.jsp @@ -1,6 +1,6 @@ <% /* - * Copyright (c) 2014-2019 LabKey Corporation + * Copyright (c) 2008-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/view/manageStatuses.jsp b/specimen/src/org/labkey/specimen/view/manageStatuses.jsp index 73a5aa25..5ec5a6dd 100644 --- a/specimen/src/org/labkey/specimen/view/manageStatuses.jsp +++ b/specimen/src/org/labkey/specimen/view/manageStatuses.jsp @@ -1,6 +1,6 @@ <% /* - * Copyright (c) 2014-2019 LabKey Corporation + * Copyright (c) 2008-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/view/relevantRequests.jsp b/specimen/src/org/labkey/specimen/view/relevantRequests.jsp index 51e1f021..2073c0f9 100644 --- a/specimen/src/org/labkey/specimen/view/relevantRequests.jsp +++ b/specimen/src/org/labkey/specimen/view/relevantRequests.jsp @@ -1,6 +1,6 @@ <% /* - * Copyright (c) 2014-2019 LabKey Corporation + * Copyright (c) 2008-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/view/requestSpecimens.jsp b/specimen/src/org/labkey/specimen/view/requestSpecimens.jsp index 335a0217..a4d7ce4c 100644 --- a/specimen/src/org/labkey/specimen/view/requestSpecimens.jsp +++ b/specimen/src/org/labkey/specimen/view/requestSpecimens.jsp @@ -1,6 +1,6 @@ <% /* - * Copyright (c) 2014-2019 LabKey Corporation + * Copyright (c) 2008-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/view/search.jsp b/specimen/src/org/labkey/specimen/view/search.jsp index 26a8b335..e4e9d5fb 100644 --- a/specimen/src/org/labkey/specimen/view/search.jsp +++ b/specimen/src/org/labkey/specimen/view/search.jsp @@ -1,6 +1,6 @@ <% /* - * Copyright (c) 2014-2019 LabKey Corporation + * Copyright (c) 2008-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/view/selectSpecimenProvider.jsp b/specimen/src/org/labkey/specimen/view/selectSpecimenProvider.jsp index 6637c7b0..f88310a1 100644 --- a/specimen/src/org/labkey/specimen/view/selectSpecimenProvider.jsp +++ b/specimen/src/org/labkey/specimen/view/selectSpecimenProvider.jsp @@ -1,6 +1,6 @@ <% /* - * Copyright (c) 2014-2019 LabKey Corporation + * Copyright (c) 2008-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/view/specimen.jsp b/specimen/src/org/labkey/specimen/view/specimen.jsp index 4d9a52eb..9692bda8 100644 --- a/specimen/src/org/labkey/specimen/view/specimen.jsp +++ b/specimen/src/org/labkey/specimen/view/specimen.jsp @@ -1,6 +1,6 @@ <% /* - * Copyright (c) 2014-2019 LabKey Corporation + * Copyright (c) 2008-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/view/specimenHeader.jsp b/specimen/src/org/labkey/specimen/view/specimenHeader.jsp index b0599197..e645fbee 100644 --- a/specimen/src/org/labkey/specimen/view/specimenHeader.jsp +++ b/specimen/src/org/labkey/specimen/view/specimenHeader.jsp @@ -1,6 +1,6 @@ <% /* - * Copyright (c) 2014-2019 LabKey Corporation + * Copyright (c) 2008-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/view/specimenVisitReport.jsp b/specimen/src/org/labkey/specimen/view/specimenVisitReport.jsp index 69f19de0..5525ddba 100644 --- a/specimen/src/org/labkey/specimen/view/specimenVisitReport.jsp +++ b/specimen/src/org/labkey/specimen/view/specimenVisitReport.jsp @@ -1,6 +1,6 @@ <% /* - * Copyright (c) 2014-2019 LabKey Corporation + * Copyright (c) 2008-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/view/updateComments.jsp b/specimen/src/org/labkey/specimen/view/updateComments.jsp index aec3a635..7eb8b5d6 100644 --- a/specimen/src/org/labkey/specimen/view/updateComments.jsp +++ b/specimen/src/org/labkey/specimen/view/updateComments.jsp @@ -1,6 +1,6 @@ <% /* - * Copyright (c) 2014-2019 LabKey Corporation + * Copyright (c) 2008-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/view/uploadSimpleSpecimens.jsp b/specimen/src/org/labkey/specimen/view/uploadSimpleSpecimens.jsp index f97cfcde..3e7a0fe6 100644 --- a/specimen/src/org/labkey/specimen/view/uploadSimpleSpecimens.jsp +++ b/specimen/src/org/labkey/specimen/view/uploadSimpleSpecimens.jsp @@ -1,6 +1,6 @@ <% /* - * Copyright (c) 2014-2019 LabKey Corporation + * Copyright (c) 2008-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/view/viewRequestsHeader.jsp b/specimen/src/org/labkey/specimen/view/viewRequestsHeader.jsp index 9c3099b3..530e1db4 100644 --- a/specimen/src/org/labkey/specimen/view/viewRequestsHeader.jsp +++ b/specimen/src/org/labkey/specimen/view/viewRequestsHeader.jsp @@ -1,6 +1,6 @@ <% /* - * Copyright (c) 2014-2019 LabKey Corporation + * Copyright (c) 2008-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/view/webPart.jsp b/specimen/src/org/labkey/specimen/view/webPart.jsp index b0634e84..e88ba99f 100644 --- a/specimen/src/org/labkey/specimen/view/webPart.jsp +++ b/specimen/src/org/labkey/specimen/view/webPart.jsp @@ -1,6 +1,6 @@ <% /* - * Copyright (c) 2014-2019 LabKey Corporation + * Copyright (c) 2008-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/writer/AbstractSpecimenWriter.java b/specimen/src/org/labkey/specimen/writer/AbstractSpecimenWriter.java index 189b389b..23445298 100644 --- a/specimen/src/org/labkey/specimen/writer/AbstractSpecimenWriter.java +++ b/specimen/src/org/labkey/specimen/writer/AbstractSpecimenWriter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013 LabKey Corporation + * Copyright (c) 2013-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/writer/LocationSpecimenWriter.java b/specimen/src/org/labkey/specimen/writer/LocationSpecimenWriter.java index c9fc5dfd..053357b0 100644 --- a/specimen/src/org/labkey/specimen/writer/LocationSpecimenWriter.java +++ b/specimen/src/org/labkey/specimen/writer/LocationSpecimenWriter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2014 LabKey Corporation + * Copyright (c) 2013-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/writer/SpecimenArchiveDataTypes.java b/specimen/src/org/labkey/specimen/writer/SpecimenArchiveDataTypes.java index 3d617466..60d6d5d3 100644 --- a/specimen/src/org/labkey/specimen/writer/SpecimenArchiveDataTypes.java +++ b/specimen/src/org/labkey/specimen/writer/SpecimenArchiveDataTypes.java @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2022-2026 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.specimen.writer; import org.labkey.api.specimen.SpecimenMigrationService; diff --git a/specimen/src/org/labkey/specimen/writer/SpecimenArchiveWriter.java b/specimen/src/org/labkey/specimen/writer/SpecimenArchiveWriter.java index e0f3f33e..b7d836ca 100644 --- a/specimen/src/org/labkey/specimen/writer/SpecimenArchiveWriter.java +++ b/specimen/src/org/labkey/specimen/writer/SpecimenArchiveWriter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2019 LabKey Corporation + * Copyright (c) 2009-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/writer/SpecimenSettingsWriter.java b/specimen/src/org/labkey/specimen/writer/SpecimenSettingsWriter.java index 85dd3446..33f99f2e 100644 --- a/specimen/src/org/labkey/specimen/writer/SpecimenSettingsWriter.java +++ b/specimen/src/org/labkey/specimen/writer/SpecimenSettingsWriter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2018 LabKey Corporation + * Copyright (c) 2013-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/writer/SpecimenWriter.java b/specimen/src/org/labkey/specimen/writer/SpecimenWriter.java index 966ec35a..c9940809 100644 --- a/specimen/src/org/labkey/specimen/writer/SpecimenWriter.java +++ b/specimen/src/org/labkey/specimen/writer/SpecimenWriter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2019 LabKey Corporation + * Copyright (c) 2009-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/src/org/labkey/specimen/writer/StandardSpecimenWriter.java b/specimen/src/org/labkey/specimen/writer/StandardSpecimenWriter.java index f2b8b0b5..c01b5a9f 100644 --- a/specimen/src/org/labkey/specimen/writer/StandardSpecimenWriter.java +++ b/specimen/src/org/labkey/specimen/writer/StandardSpecimenWriter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2018 LabKey Corporation + * Copyright (c) 2009-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/specimen/test/src/org/labkey/test/pages/specimen/ConfigureSpecimenImportPage.java b/specimen/test/src/org/labkey/test/pages/specimen/ConfigureSpecimenImportPage.java index 7114172d..a686842e 100644 --- a/specimen/test/src/org/labkey/test/pages/specimen/ConfigureSpecimenImportPage.java +++ b/specimen/test/src/org/labkey/test/pages/specimen/ConfigureSpecimenImportPage.java @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2026 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.test.pages.specimen; import org.labkey.test.Locator; diff --git a/specimen/test/src/org/labkey/test/tests/specimen/QueryBasedSpecimenImportTest.java b/specimen/test/src/org/labkey/test/tests/specimen/QueryBasedSpecimenImportTest.java index 8e6df164..92f0105e 100644 --- a/specimen/test/src/org/labkey/test/tests/specimen/QueryBasedSpecimenImportTest.java +++ b/specimen/test/src/org/labkey/test/tests/specimen/QueryBasedSpecimenImportTest.java @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2026 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.test.tests.specimen; import org.junit.Before; diff --git a/study/test/src/org/labkey/test/tests/study/SpecimenMergeTest.java b/study/test/src/org/labkey/test/tests/study/SpecimenMergeTest.java index d77db130..98907654 100644 --- a/study/test/src/org/labkey/test/tests/study/SpecimenMergeTest.java +++ b/study/test/src/org/labkey/test/tests/study/SpecimenMergeTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2019 LabKey Corporation + * Copyright (c) 2016-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/study/test/src/org/labkey/test/tests/study/SpecimenReplaceTest.java b/study/test/src/org/labkey/test/tests/study/SpecimenReplaceTest.java index a8f9d19c..0f431007 100644 --- a/study/test/src/org/labkey/test/tests/study/SpecimenReplaceTest.java +++ b/study/test/src/org/labkey/test/tests/study/SpecimenReplaceTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2019 LabKey Corporation + * Copyright (c) 2016-2026 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. From c4c39791ab8a1f45387b57642415d55597f5237d Mon Sep 17 00:00:00 2001 From: Josh Eckels Date: Tue, 9 Jun 2026 07:56:40 -0700 Subject: [PATCH 480/494] Specimen module cleanup Original PR: https://github.com/LabKey/platform/pull/7733 #### Rationale Lint cleanup and SQL improvements #### Changes - Clear assorted IntelliJ warnings - Use JDBC parameter --- .../labkey/specimen/importer/ImportableColumn.java | 6 +++--- .../specimen/importer/RequestabilityManager.java | 11 ++--------- .../specimen/importer/SimpleSpecimenImporter.java | 14 -------------- 3 files changed, 5 insertions(+), 26 deletions(-) diff --git a/specimen/src/org/labkey/specimen/importer/ImportableColumn.java b/specimen/src/org/labkey/specimen/importer/ImportableColumn.java index c556a687..ee71e0fb 100644 --- a/specimen/src/org/labkey/specimen/importer/ImportableColumn.java +++ b/specimen/src/org/labkey/specimen/importer/ImportableColumn.java @@ -24,7 +24,7 @@ public class ImportableColumn private final boolean _maskOnExport; private final boolean _unique; private final int _size; - private final Class _javaClass; + private final Class _javaClass; private final JdbcType _jdbcType; private Object _defaultValue = null; @@ -88,7 +88,7 @@ public ImportableColumn(String tsvColumnName, Collection tsvColumnAliase } // Can't use standard JdbcType.valueOf() method since this uses contains() - private static Class determineJavaType(String dbType) + private static Class determineJavaType(String dbType) { if (dbType.contains(ImportTypes.DATETIME_TYPE)) throw new IllegalStateException("Java types for DateTime/Timestamp columns should be previously initialized."); @@ -148,7 +148,7 @@ public boolean isUnique() return _unique; } - public Class getJavaClass() + public Class getJavaClass() { return _javaClass; } diff --git a/specimen/src/org/labkey/specimen/importer/RequestabilityManager.java b/specimen/src/org/labkey/specimen/importer/RequestabilityManager.java index cc6ccdc0..3b718aca 100644 --- a/specimen/src/org/labkey/specimen/importer/RequestabilityManager.java +++ b/specimen/src/org/labkey/specimen/importer/RequestabilityManager.java @@ -395,8 +395,6 @@ public RequestableRule(Container container) public int updateRequestability(User user, List vials) throws InvalidRuleException { TableInfo tableInfoVial = SpecimenSchema.get().getTableInfoVial(_container); - if (null == tableInfoVial) - throw new IllegalStateException("Expected Vial table to exist."); SQLFragment reason = getAvailabilityReason(); SQLFragment updateSQL = new SQLFragment("UPDATE "); @@ -573,7 +571,7 @@ public SQLFragment getFilterSQL(Container container, User user, List vials @Override public SQLFragment getAvailabilityReason() { - return new SQLFragment("'This vial is " + getMarkType().getLabel().toLowerCase() + " because it was found in the set called \"" + _queryName + "\".'"); + return new SQLFragment("?", "This vial is " + getMarkType().getLabel().toLowerCase() + " because it was found in the set called \"" + _queryName + "\"."); } @Override @@ -695,8 +693,6 @@ public LockedWhileProcessingRule(Container container) public SQLFragment getFilterSQL(Container container, User user, List vials) { TableInfo tableInfoVial = SpecimenSchema.get().getTableInfoVial(_container); - if (null == tableInfoVial) - throw new IllegalStateException("Expected Vial table to exist."); SQLFragment sql = new SQLFragment(); if (vials != null && !vials.isEmpty()) @@ -792,8 +788,6 @@ private void updateRequestability(Container container, User user, boolean resetT if (resetToAvailable) { TableInfo tableInfoVial = SpecimenSchema.get().getTableInfoVial(container); - if (null == tableInfoVial) - throw new IllegalStateException("Expected Vial table to exist."); if (logger != null) logger.info("\tResetting vials to default available state."); @@ -847,10 +841,9 @@ public void updateRequestability(Container container, User user, boolean resetTo public static String makeSpecimenUnavailableMessage(Vial vial, @Nullable String additionalText) { - String message = String.format("Specimen %s%s%s", + return String.format("Specimen %s%s%s", vial.getGlobalUniqueId(), null != vial.getAvailabilityReason() ? vial.getAvailabilityReason().replaceFirst("This vial", "") : " is not available.", null != additionalText ? " " + additionalText : ""); - return message; } } diff --git a/specimen/src/org/labkey/specimen/importer/SimpleSpecimenImporter.java b/specimen/src/org/labkey/specimen/importer/SimpleSpecimenImporter.java index 09d4f5f5..13a5cfd5 100644 --- a/specimen/src/org/labkey/specimen/importer/SimpleSpecimenImporter.java +++ b/specimen/src/org/labkey/specimen/importer/SimpleSpecimenImporter.java @@ -84,11 +84,6 @@ public class SimpleSpecimenImporter extends SpecimenImporter private final Map _columnLabels; private final TimepointType _timepointType; - public SimpleSpecimenImporter(Container container, User user) - { - this(container, user, TimepointType.DATE, "Subject"); - } - public SimpleSpecimenImporter(Container container, User user, TimepointType timepointType, String participantIdLabel) { super(container, user); @@ -130,15 +125,6 @@ public void fixupSpecimenColumns(TabLoader tl) throws IOException tl.setColumns(mappedCols); } - // UNDONE: Converting values belongs in _process - public List> fixupSpecimenRows(List> rows) - { - List> result = new ArrayList<>(); - for (Map row : rows) - result.add(fixupSpecimenRow(row)); - return result; - } - private Map fixupSpecimenRow(Map row) { Map result = new HashMap<>(); From 4530b794e1c77013d124ca34687eb0ee03d263b5 Mon Sep 17 00:00:00 2001 From: Josh Eckels Date: Wed, 10 Jun 2026 20:57:20 -0700 Subject: [PATCH 481/494] Improve scoping checks for objects Original PR: https://github.com/LabKey/platform/pull/7739 #### Rationale We can improve our parameter validation #### Changes - New helpful base class for integration tests, `AbstractContainerScopingTest` - New test coverage - Assorted scoping checks --- .../org/labkey/specimen/SpecimenModule.java | 5 +- .../actions/ShowGroupMembersAction.java | 4 + .../specimen/actions/SpecimenController.java | 93 ++++++++++++++++++- .../DefaultRequirementProvider.java | 12 ++- .../SpecimenRequestRequirementProvider.java | 48 ++++++++++ 5 files changed, 158 insertions(+), 4 deletions(-) diff --git a/specimen/src/org/labkey/specimen/SpecimenModule.java b/specimen/src/org/labkey/specimen/SpecimenModule.java index 9dbcf367..a8341516 100644 --- a/specimen/src/org/labkey/specimen/SpecimenModule.java +++ b/specimen/src/org/labkey/specimen/SpecimenModule.java @@ -72,6 +72,7 @@ import org.labkey.specimen.importer.SpecimenSettingsImporter; import org.labkey.specimen.model.SpecimenRequestEventType; import org.labkey.specimen.pipeline.SpecimenPipeline; +import org.labkey.specimen.requirements.SpecimenRequestRequirementProvider; import org.labkey.specimen.query.SpecimenPivotByDerivativeType; import org.labkey.specimen.query.SpecimenPivotByPrimaryType; import org.labkey.specimen.query.SpecimenPivotByRequestingLocation; @@ -333,7 +334,9 @@ public Set getUnitTests() public @NotNull Set getIntegrationTests() { return Set.of( - SpecimenImporter.TestCase.class + SpecimenImporter.TestCase.class, + SpecimenRequestRequirementProvider.ContainerScopingTestCase.class, + SpecimenController.ContainerScopingTestCase.class ); } } \ No newline at end of file diff --git a/specimen/src/org/labkey/specimen/actions/ShowGroupMembersAction.java b/specimen/src/org/labkey/specimen/actions/ShowGroupMembersAction.java index ddf84246..f728e50d 100644 --- a/specimen/src/org/labkey/specimen/actions/ShowGroupMembersAction.java +++ b/specimen/src/org/labkey/specimen/actions/ShowGroupMembersAction.java @@ -92,6 +92,10 @@ public boolean handlePost(UpdateGroupForm form, BindException errors) throws Exc SpecimenRequestActor actor = getActor(form); LocationImpl location = getLocation(form); + // getActor is container-scoped; null means the actorId doesn't belong to this folder + if (actor == null) + throw new NotFoundException(); + if (emailsToDelete != null && emailsToDelete.length > 0) { List invalidEmails = new ArrayList<>(); diff --git a/specimen/src/org/labkey/specimen/actions/SpecimenController.java b/specimen/src/org/labkey/specimen/actions/SpecimenController.java index 0682600a..f3d79169 100644 --- a/specimen/src/org/labkey/specimen/actions/SpecimenController.java +++ b/specimen/src/org/labkey/specimen/actions/SpecimenController.java @@ -14,6 +14,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.json.JSONObject; +import org.junit.Test; import org.labkey.api.action.ApiResponse; import org.labkey.api.action.ApiSimpleResponse; import org.labkey.api.action.ExportAction; @@ -82,6 +83,7 @@ import org.labkey.api.security.RequiresPermission; import org.labkey.api.security.User; import org.labkey.api.security.ValidEmail; +import org.labkey.api.security.permissions.AbstractContainerScopingTest; import org.labkey.api.security.permissions.AdminPermission; import org.labkey.api.security.permissions.ReadPermission; import org.labkey.api.security.permissions.UpdatePermission; @@ -4611,7 +4613,8 @@ public boolean handlePost(RequirementForm form, BindException errors) throws Exc { SpecimenRequestRequirement requirement = SpecimenRequestRequirementProvider.get().getRequirement(getContainer(), form.getRequirementId()); - if (requirement.getRequestId() == form.getId()) + // getRequirement is container-scoped; null means the requirementId doesn't belong to this folder + if (requirement != null && requirement.getRequestId() == form.getId()) { SpecimenRequestManager.get().deleteRequestRequirement(getUser(), requirement); return true; @@ -5177,6 +5180,9 @@ public boolean handlePost(EmailSpecimenListForm form, BindException errors) thro ids[i] = Integer.parseInt(idStrs[i]); LocationImpl originatingOrProvidingLocation = LocationManager.get().getLocation(getContainer(), ids[0]); SpecimenRequestActor notifyActor = SpecimenRequestRequirementProvider.get().getActor(getContainer(), ids[1]); + // getActor is container-scoped; null means the actorId doesn't belong to this folder + if (notifyActor == null) + throw new NotFoundException("No notification actor found for id " + ids[1] + " in this folder"); LocationImpl notifyLocation = null; if (notifyActor.isPerSite() && ids[2] >= 0) notifyLocation = LocationManager.get().getLocation(getContainer(), ids[2]); @@ -5757,4 +5763,89 @@ public void addNavTrail(NavTree root) root.addChild("Insert " + _form.getQueryName()); } } + + /** + * Verifies that actions resolving a specimen object by its global rowId reject ids that belong to a different + * container, even when the caller has the action's required permission in the current folder. These exercise the + * action-level guards that depend on the container scoping enforced by {@link SpecimenRequestRequirementProvider}. + */ + public static class ContainerScopingTestCase extends AbstractContainerScopingTest + { + // Actors are required by requirements through a NOT NULL foreign key, so several fixtures need one + private SpecimenRequestActor createActor(Container c, String label) + { + SpecimenRequestActor actor = new SpecimenRequestActor(); + actor.setContainer(c); + actor.setLabel(label); + return actor.create(getAdmin()); + } + + @Test + public void testShowGroupMembersActionRejectsForeignActor() throws Exception + { + Container folderA = createContainer("A"); + Container folderB = createContainer("B"); + + // An actor that lives in folder A + SpecimenRequestActor actor = createActor(folderA, "Group members scoping actor"); + int actorId = actor.getRowId(); + + // Addressing the action through folder B, where the actor does not live, must 404 rather than expose it + ActionURL foreignUrl = new ActionURL(ShowGroupMembersAction.class, folderB).addParameter("id", String.valueOf(actorId)); + assertStatus(HttpServletResponse.SC_NOT_FOUND, post(foreignUrl, getAdmin())); + + // Positive control: the same request through the actor's own folder is accepted (redirect on success), + // proving the guard rejects only the cross-container case rather than every request + ActionURL ownUrl = new ActionURL(ShowGroupMembersAction.class, folderA).addParameter("id", String.valueOf(actorId)); + assertStatus(HttpServletResponse.SC_FOUND, post(ownUrl, getAdmin())); + } + + @Test + public void testDeleteRequirementActionRejectsForeignRequirement() throws Exception + { + Container folderA = createContainer("A"); + Container folderB = createContainer("B"); + SpecimenRequestRequirementProvider provider = SpecimenRequestRequirementProvider.get(); + + // Build a real request chain in folder A: status -> request -> requirement. Deleting a requirement logs a + // request event whose RequestId is a foreign key into SampleRequest, so the request row must actually + // exist (a fake id would fail the FK and mask what this test is checking). Capture each insert's return so + // we have the generated rowId. + SpecimenRequestStatus status = new SpecimenRequestStatus(); + status.setContainer(folderA); + status.setLabel("Delete requirement scoping status"); + status.setSortOrder(1); // non-null and >= 0 so the bean isn't treated as a system status + status = Table.insert(getAdmin(), SpecimenSchema.get().getTableInfoSampleRequestStatus(), status); + + SpecimenRequest request = new SpecimenRequest(); + request.setContainer(folderA); + request.setStatusId(status.getRowId()); + request = SpecimenRequestManager.get().createRequest(getAdmin(), request, false); + int requestId = request.getRowId(); + + SpecimenRequestActor actor = createActor(folderA, "Delete requirement scoping actor"); + SpecimenRequestRequirement requirement = new SpecimenRequestRequirement(); + requirement.setContainer(folderA); + requirement.setRequestId(requestId); + requirement.setActorId(actor.getRowId()); + requirement.setDescription("Delete requirement scoping test"); + requirement = requirement.persist(getAdmin(), GUID.makeGUID()); + int requirementId = requirement.getRowId(); + + // Deleting through folder B, where the requirement does not live, must be a no-op: the row survives + ActionURL foreignUrl = new ActionURL(DeleteRequirementAction.class, folderB) + .addParameter("id", String.valueOf(requestId)) + .addParameter("requirementId", String.valueOf(requirementId)); + post(foreignUrl, getAdmin()); + assertNotNull("Cross-container delete must not remove the requirement", provider.getRequirement(folderA, requirementId)); + + // Positive control: deleting through the requirement's own folder removes it, proving the guard rejects + // only the cross-container case rather than every delete + ActionURL ownUrl = new ActionURL(DeleteRequirementAction.class, folderA) + .addParameter("id", String.valueOf(requestId)) + .addParameter("requirementId", String.valueOf(requirementId)); + post(ownUrl, getAdmin()); + assertNull("Same-container delete should remove the requirement", provider.getRequirement(folderA, requirementId)); + } + } } diff --git a/specimen/src/org/labkey/specimen/requirements/DefaultRequirementProvider.java b/specimen/src/org/labkey/specimen/requirements/DefaultRequirementProvider.java index 803e98fd..2111948c 100644 --- a/specimen/src/org/labkey/specimen/requirements/DefaultRequirementProvider.java +++ b/specimen/src/org/labkey/specimen/requirements/DefaultRequirementProvider.java @@ -100,7 +100,11 @@ private synchronized String getDefaultRequirementPlaceholder(final Container con @Override public R getRequirement(Container container, Object requirementPrimaryKey) { - return new TableSelector(getRequirementTableInfo()).getObject(requirementPrimaryKey, _requirementClass); + R requirement = new TableSelector(getRequirementTableInfo()).getObject(requirementPrimaryKey, _requirementClass); + // The lookup is by global primary key; reject rows that don't belong to the requested container + if (requirement != null && !container.equals(requirement.getContainer())) + return null; + return requirement; } public R[] getRequirements(Container container, String ownerEntityId) @@ -133,7 +137,11 @@ public A[] getActors(Container c) @Override public A getActor(Container c, Object primaryKey) { - return new TableSelector(getActorTableInfo()).getObject(primaryKey, _actorClass); + A actor = new TableSelector(getActorTableInfo()).getObject(primaryKey, _actorClass); + // The lookup is by global primary key; reject rows that don't belong to the requested container + if (actor != null && !c.equals(actor.getContainer())) + return null; + return actor; } @Override diff --git a/specimen/src/org/labkey/specimen/requirements/SpecimenRequestRequirementProvider.java b/specimen/src/org/labkey/specimen/requirements/SpecimenRequestRequirementProvider.java index 4af4b5ab..c7be11bb 100644 --- a/specimen/src/org/labkey/specimen/requirements/SpecimenRequestRequirementProvider.java +++ b/specimen/src/org/labkey/specimen/requirements/SpecimenRequestRequirementProvider.java @@ -16,9 +16,12 @@ package org.labkey.specimen.requirements; +import org.junit.Test; import org.labkey.api.data.Container; import org.labkey.api.data.TableInfo; +import org.labkey.api.security.permissions.AbstractContainerScopingTest; import org.labkey.api.specimen.SpecimenSchema; +import org.labkey.api.util.GUID; import org.labkey.specimen.model.SpecimenRequestActor; import java.util.Collection; @@ -99,4 +102,49 @@ public Set getActorsInUseSet(Container container) ids.add(actor.getRowId()); return ids; } + + public static class ContainerScopingTestCase extends AbstractContainerScopingTest + { + @Test + public void testGetActorContainerScoping() + { + Container folderA = createContainer("A"); + Container folderB = createContainer("B"); + SpecimenRequestRequirementProvider provider = SpecimenRequestRequirementProvider.get(); + + SpecimenRequestActor actor = new SpecimenRequestActor(); + actor.setContainer(folderA); + actor.setLabel("Scoping test actor"); + actor = actor.create(getAdmin()); + int rowId = actor.getRowId(); + + assertNotNull("Actor should be visible from its own container", provider.getActor(folderA, rowId)); + assertNull("Actor must NOT be visible from another container", provider.getActor(folderB, rowId)); + } + + @Test + public void testGetRequirementContainerScoping() + { + Container folderA = createContainer("A"); + Container folderB = createContainer("B"); + SpecimenRequestRequirementProvider provider = SpecimenRequestRequirementProvider.get(); + + // A requirement references an actor through a NOT NULL foreign key, so create one in the same folder first + SpecimenRequestActor actor = new SpecimenRequestActor(); + actor.setContainer(folderA); + actor.setLabel("Requirement scoping test actor"); + actor = actor.create(getAdmin()); + + SpecimenRequestRequirement requirement = new SpecimenRequestRequirement(); + requirement.setContainer(folderA); + requirement.setRequestId(-1); // no real request row is needed for a primary-key lookup + requirement.setActorId(actor.getRowId()); + requirement.setDescription("Requirement scoping test"); + requirement = requirement.persist(getAdmin(), GUID.makeGUID()); + int rowId = requirement.getRowId(); + + assertNotNull("Requirement should be visible from its own container", provider.getRequirement(folderA, rowId)); + assertNull("Requirement must NOT be visible from another container", provider.getRequirement(folderB, rowId)); + } + } } From 3dc5ac99763951c51d82c12ab6913138ad9d596f Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Tue, 16 Jun 2026 14:15:46 -0700 Subject: [PATCH 482/494] Modernize specimen, report, and messages rendering Original PR: https://github.com/LabKey/platform/pull/7746 --- .../specimen/actions/SpecimenController.java | 12 ++-- .../specimen/actions/SpecimenHeaderBean.java | 26 ++++---- .../specimen/view/manageRequirement.jsp | 2 +- .../labkey/specimen/view/specimenHeader.jsp | 62 +++++++++++-------- 4 files changed, 58 insertions(+), 44 deletions(-) diff --git a/specimen/src/org/labkey/specimen/actions/SpecimenController.java b/specimen/src/org/labkey/specimen/actions/SpecimenController.java index f3d79169..9d48a30e 100644 --- a/specimen/src/org/labkey/specimen/actions/SpecimenController.java +++ b/specimen/src/org/labkey/specimen/actions/SpecimenController.java @@ -5546,6 +5546,8 @@ public void addNavTrail(NavTree root) } } + public record PtidVisit(String ptid, String visit){} + @RequiresPermission(ReadPermission.class) public class SelectedSpecimensAction extends QueryViewAction { @@ -5561,26 +5563,26 @@ public SelectedSpecimensAction() protected ModelAndView getHtmlView(SpecimenViewTypeForm form, BindException errors) throws Exception { Study study = getStudyRedirectIfNull(); - Set> ptidVisits = new HashSet<>(); + Set ptidVisits = new HashSet<>(); for (ParticipantDataset pd : getFilterPds()) { if (pd.getSequenceNum() == null) { - ptidVisits.add(new Pair<>(pd.getParticipantId(), null)); + ptidVisits.add(new PtidVisit(pd.getParticipantId(), null)); } else if (study.getTimepointType() != TimepointType.VISIT && pd.getVisitDate() != null) { - ptidVisits.add(new Pair<>(pd.getParticipantId(), DateUtil.formatDate(pd.getContainer(), pd.getVisitDate()))); + ptidVisits.add(new PtidVisit(pd.getParticipantId(), DateUtil.formatDate(pd.getContainer(), pd.getVisitDate()))); } else { Visit visit = pd.getSequenceNum() != null ? StudyInternalService.get().getVisitForSequence(study, pd.getSequenceNum()) : null; - ptidVisits.add(new Pair<>(pd.getParticipantId(), visit != null ? visit.getLabel() : "" + StudyInternalService.get().formatSequenceNum(pd.getSequenceNum()))); + ptidVisits.add(new PtidVisit(pd.getParticipantId(), visit != null ? visit.getLabel() : StudyInternalService.get().formatSequenceNum(pd.getSequenceNum()))); } } SpecimenQueryView view = createInitializedQueryView(form, errors, form.getExportType() != null, null); JspView header = new JspView<>("/org/labkey/specimen/view/specimenHeader.jsp", - new SpecimenHeaderBean(getViewContext(), view, ptidVisits)); + new SpecimenHeaderBean(getViewContext(), view, ptidVisits)); return new VBox(header, view); } diff --git a/specimen/src/org/labkey/specimen/actions/SpecimenHeaderBean.java b/specimen/src/org/labkey/specimen/actions/SpecimenHeaderBean.java index fe32d12d..fd0cbd57 100644 --- a/specimen/src/org/labkey/specimen/actions/SpecimenHeaderBean.java +++ b/specimen/src/org/labkey/specimen/actions/SpecimenHeaderBean.java @@ -5,13 +5,13 @@ import org.labkey.api.query.FieldKey; import org.labkey.api.query.QueryService; import org.labkey.api.specimen.SpecimenQuerySchema; -import org.labkey.specimen.query.SpecimenQueryView; import org.labkey.api.study.Study; import org.labkey.api.study.StudyService; -import org.labkey.api.util.Pair; import org.labkey.api.view.ActionURL; import org.labkey.api.view.NotFoundException; import org.labkey.api.view.ViewContext; +import org.labkey.specimen.actions.SpecimenController.PtidVisit; +import org.labkey.specimen.query.SpecimenQueryView; import java.util.Collections; import java.util.Iterator; @@ -24,7 +24,7 @@ public final class SpecimenHeaderBean private final ActionURL _otherViewURL; private final ViewContext _viewContext; private final boolean _showingVials; - private final Set> _filteredPtidVisits; + private final Set _ptidVisits; private Integer _selectedRequest; @@ -33,7 +33,7 @@ public SpecimenHeaderBean(ViewContext context, SpecimenQueryView view) this(context, view, Collections.emptySet()); } - public SpecimenHeaderBean(ViewContext context, SpecimenQueryView view, Set> filteredPtidVisits) throws RuntimeException + public SpecimenHeaderBean(ViewContext context, SpecimenQueryView view, Set ptidVisits) throws RuntimeException { Map params = context.getRequest().getParameterMap(); @@ -90,11 +90,11 @@ public SpecimenHeaderBean(ViewContext context, SpecimenQueryView view, Set> getFilteredPtidVisits() + public Set getPtidVisits() { - return _filteredPtidVisits; + return _ptidVisits; } public boolean isSingleVisitFilter() { - if (getFilteredPtidVisits().isEmpty()) + if (getPtidVisits().isEmpty()) return false; - Iterator> visitIt = getFilteredPtidVisits().iterator(); - String firstVisit = visitIt.next().getValue(); - while (visitIt.hasNext()) + Iterator ptidVisit = getPtidVisits().iterator(); + String firstVisit = ptidVisit.next().visit(); + while (ptidVisit.hasNext()) { - if (!Objects.equals(firstVisit, visitIt.next().getValue())) + if (!Objects.equals(firstVisit, ptidVisit.next().visit())) return false; } return true; diff --git a/specimen/src/org/labkey/specimen/view/manageRequirement.jsp b/specimen/src/org/labkey/specimen/view/manageRequirement.jsp index 68109262..6721bbc6 100644 --- a/specimen/src/org/labkey/specimen/view/manageRequirement.jsp +++ b/specimen/src/org/labkey/specimen/view/manageRequirement.jsp @@ -84,7 +84,7 @@
- + <% if (!bean.isRequestManager()) diff --git a/specimen/src/org/labkey/specimen/view/specimenHeader.jsp b/specimen/src/org/labkey/specimen/view/specimenHeader.jsp index b0599197..b1e2c7b5 100644 --- a/specimen/src/org/labkey/specimen/view/specimenHeader.jsp +++ b/specimen/src/org/labkey/specimen/view/specimenHeader.jsp @@ -15,16 +15,18 @@ * limitations under the License. */ %> -<%@ page import="org.labkey.api.security.permissions.AdminPermission"%> -<%@ page import="org.labkey.api.study.StudyService"%> -<%@ page import="org.labkey.api.study.StudyUrls"%> -<%@ page import="org.labkey.api.util.Pair"%> +<%@ page import="org.apache.commons.lang3.StringUtils" %> +<%@ page import="org.labkey.api.security.permissions.AdminPermission" %> +<%@ page import="org.labkey.api.study.StudyService" %> +<%@ page import="org.labkey.api.study.StudyUrls" %> +<%@ page import="org.labkey.api.util.HtmlStringBuilder" %> <%@ 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.specimen.actions.ShowSearchAction" %> <%@ page import="org.labkey.specimen.actions.SpecimenController" %> +<%@ page import="org.labkey.specimen.actions.SpecimenController.PtidVisit" %> <%@ page import="org.labkey.specimen.actions.SpecimenController.SpecimensAction" %> <%@ page import="org.labkey.specimen.actions.SpecimenHeaderBean" %> <%@ page import="java.util.Iterator" %> @@ -74,47 +76,57 @@ <%=link("Search", ShowSearchAction.getShowSearchURL(getContainer(), bean.isShowingVials()))%>  <%=link("Reports", urlFor(SpecimenController.AutoReportListAction.class)) %> <% - if (!bean.getFilteredPtidVisits().isEmpty()) + if (!bean.getPtidVisits().isEmpty()) { // get the first visit label: - StringBuilder filterString = new StringBuilder(); - filterString.append("This view is displaying specimens only from "); - boolean usePlural = bean.getFilteredPtidVisits().size() != 1; + HtmlStringBuilder builder = HtmlStringBuilder.of() + .unsafeAppend("") + .append("This view is displaying specimens only from "); + boolean usePlural = bean.getPtidVisits().size() != 1; if (bean.isSingleVisitFilter()) { - filterString.append(h((usePlural?subjectNounPlural:subjectNounSingle).toLowerCase())).append(" "); - for (Iterator> it = bean.getFilteredPtidVisits().iterator(); it.hasNext();) + builder.append((usePlural ? subjectNounPlural : subjectNounSingle).toLowerCase()) + .append(" "); + for (Iterator it = bean.getPtidVisits().iterator(); it.hasNext();) { - String ptid = it.next().getKey(); - filterString.append(ptid); + String ptid = it.next().ptid(); + builder.append(ptid); if (it.hasNext()) - filterString.append(", "); + builder.append(", "); } - String visit = bean.getFilteredPtidVisits().iterator().next().getValue(); + String visit = bean.getPtidVisits().iterator().next().visit(); if (visit != null) - filterString.append(" at visit ").append(visit); - filterString.append(".
"); + builder.append(" at visit ").append(visit); + + builder.append(".") + .unsafeAppend("

"); } else { - filterString.append(" the following ").append(h(subjectNounSingle.toLowerCase())).append("/visit ").append(usePlural?"pairs":"pair").append(":
"); - for (Iterator> it = bean.getFilteredPtidVisits().iterator(); it.hasNext();) + builder.append(" the following ") + .append(subjectNounSingle.toLowerCase()) + .append("/visit ").append(usePlural ? "pairs" : "pair") + .append(":") + .unsafeAppend("
"); + for (Iterator it = bean.getPtidVisits().iterator(); it.hasNext();) { - Pair ptidVisit = it.next(); - filterString.append(ptidVisit.getKey()).append("/").append(ptidVisit.getValue()); + PtidVisit ptidVisit = it.next(); + builder.append(ptidVisit.ptid()) + .append("/") + .append(StringUtils.trimToEmpty(ptidVisit.visit())); if (it.hasNext()) - filterString.append(", "); + builder.append(", "); } - filterString.append("."); + builder.append("."); } - ActionURL noFitlerUrl = getViewContext().cloneActionURL().setAction(SpecimensAction.class); + ActionURL noFilterUrl = getViewContext().cloneActionURL().setAction(SpecimensAction.class); %>

<%= unsafe(statuses != null && statuses.size()> 0 ? "Step Number" : " ") %><%= unsafe(statuses != null && !statuses.isEmpty() ? "Step Number" : " ") %> Status Name Final State<%= helpPopup("Final States", "States are final if they indicate no further processing will occur for a request. For example, 'Completed', or 'Rejected' could be final states.")%> Lock Specimens<%= helpPopup("Locked Specimen States", "Specifies whether specimens should be available for additional requests while in each status.")%>  
Description<%= unsafe(requirement.getDescription()) %><%= h(requirement.getDescription()) %>
- +
<%= unsafe(filterString.toString()) %>
<%=builder%>

-<%= link("Remove " + subjectNounSingle + "/Visit Filter", noFitlerUrl)%><% +<%= link("Remove " + subjectNounSingle + "/Visit Filter", noFilterUrl)%><% } %>
From d36f7bec3ff35af0b0c1c46dac5a9508c3fd7460 Mon Sep 17 00:00:00 2001 From: Josh Eckels Date: Tue, 16 Jun 2026 16:44:55 -0700 Subject: [PATCH 483/494] Improve scoping checks for objects, round 2 Original PR: https://github.com/LabKey/platform/pull/7749 #### Rationale We can improve our parameter validation #### Changes - New test coverage - Assorted scoping checks --- .../actions/SpecimenApiController.java | 1 + .../specimen/actions/SpecimenController.java | 78 +++++++++++++++++++ 2 files changed, 79 insertions(+) diff --git a/specimen/src/org/labkey/specimen/actions/SpecimenApiController.java b/specimen/src/org/labkey/specimen/actions/SpecimenApiController.java index e9a7dd6d..2621a19e 100644 --- a/specimen/src/org/labkey/specimen/actions/SpecimenApiController.java +++ b/specimen/src/org/labkey/specimen/actions/SpecimenApiController.java @@ -286,6 +286,7 @@ public class GetRequestAction extends ReadOnlyApiAction @Override public ApiResponse execute(RequestIdForm requestIdForm, BindException errors) { + // OK for anyone with read access to see any request in this container, even if they didn't create it SpecimenRequest request = getRequest(getUser(), getContainer(), requestIdForm.getRequestId(), false, false); final Map response = new HashMap<>(); response.put("request", request != null ? getRequestResponse(getViewContext(), request) : null); diff --git a/specimen/src/org/labkey/specimen/actions/SpecimenController.java b/specimen/src/org/labkey/specimen/actions/SpecimenController.java index 9d48a30e..059d63a9 100644 --- a/specimen/src/org/labkey/specimen/actions/SpecimenController.java +++ b/specimen/src/org/labkey/specimen/actions/SpecimenController.java @@ -63,6 +63,7 @@ import org.labkey.api.gwt.client.AuditBehaviorType; import org.labkey.api.module.FolderType; import org.labkey.api.module.Module; +import org.labkey.api.module.ModuleLoader; import org.labkey.api.pipeline.PipeRoot; import org.labkey.api.pipeline.PipelineService; import org.labkey.api.pipeline.PipelineStatusUrls; @@ -87,6 +88,7 @@ import org.labkey.api.security.permissions.AdminPermission; import org.labkey.api.security.permissions.ReadPermission; import org.labkey.api.security.permissions.UpdatePermission; +import org.labkey.api.security.roles.ReaderRole; import org.labkey.api.specimen.SpecimenQuerySchema; import org.labkey.api.specimen.SpecimenSchema; import org.labkey.api.specimen.Vial; @@ -151,8 +153,10 @@ import org.labkey.specimen.RequestedSpecimens; import org.labkey.specimen.SpecimenManager; import org.labkey.specimen.SpecimenRequestException; +import org.labkey.specimen.SpecimenModule; import org.labkey.specimen.SpecimenRequestManager; import org.labkey.specimen.SpecimenRequestStatus; +import org.labkey.specimen.security.roles.SpecimenRequesterRole; import org.labkey.specimen.importer.RequestabilityManager; import org.labkey.specimen.importer.SimpleSpecimenImporter; import org.labkey.specimen.model.ExtendedSpecimenRequestView; @@ -3801,6 +3805,8 @@ public boolean handlePost(final ManageRequestStatusForm form, BindException erro if (_specimenRequest == null) throw new NotFoundException(); + requiresEditRequestPermissions(_specimenRequest); + boolean statusChanged = form.getStatus() != _specimenRequest.getStatusId(); boolean detailsChanged = !nullSafeEqual(form.getRequestDescription(), _specimenRequest.getComments()); @@ -5849,5 +5855,77 @@ public void testDeleteRequirementActionRejectsForeignRequirement() throws Except post(ownUrl, getAdmin()); assertNull("Same-container delete should remove the requirement", provider.getRequirement(folderA, requirementId)); } + + @Test + public void testDeleteActorActionRejectsForeignActor() throws Exception + { + Container folderA = createContainer("A"); + Container folderB = createContainer("B"); + SpecimenRequestRequirementProvider provider = SpecimenRequestRequirementProvider.get(); + + // An actor that lives in folder A + SpecimenRequestActor actor = createActor(folderA, "Delete actor scoping actor"); + int actorId = actor.getRowId(); + + // Deleting through folder B, where the actor does not live, must be a graceful no-op rather than a 500: + // the fix makes getActor container-scoped, so handlePost sees null and skips actor.delete() instead of + // NPEing. The action still redirects (302) and the actor must survive in its own folder. + ActionURL foreignUrl = new ActionURL(DeleteActorAction.class, folderB) + .addParameter("id", String.valueOf(actorId)); + assertStatus(HttpServletResponse.SC_FOUND, post(foreignUrl, getAdmin())); + assertNotNull("Cross-container delete must not remove the actor", provider.getActor(folderA, actorId)); + + // Positive control: deleting through the actor's own folder removes it, proving the guard rejects only the + // cross-container case rather than every delete + ActionURL ownUrl = new ActionURL(DeleteActorAction.class, folderA) + .addParameter("id", String.valueOf(actorId)); + assertStatus(HttpServletResponse.SC_FOUND, post(ownUrl, getAdmin())); + assertNull("Same-container delete should remove the actor", provider.getActor(folderA, actorId)); + } + + @Test + public void testManageRequestStatusRequiresEditPermission() throws Exception + { + // ManageRequestStatusAction is annotated @RequiresPermission(RequestSpecimensPermission.class), which only + // proves the caller may make requests. However, a requester shouldn't be able to change ANOTHER user's request + // + // SpecimenRequesterRole (RequestSpecimensPermission only, no ManageRequestsPermission) is applicable only + // in a study folder that has the Specimen module, so stand up a minimal study. + Container folder = createContainer("ManageStatus"); + Set modules = new HashSet<>(folder.getActiveModules()); + modules.add(ModuleLoader.getInstance().getModule("study")); + modules.add(ModuleLoader.getInstance().getModule(SpecimenModule.NAME)); + folder.setActiveModules(modules, getAdmin()); + StudyService.get().createStudy(folder, getAdmin(), "Specimen scoping study", TimepointType.VISIT, true); + + // A request OWNED BY THE ADMIN (not the attacker), in a freshly created status + SpecimenRequestStatus status = new SpecimenRequestStatus(); + status.setContainer(folder); + status.setLabel("Manage status scoping status"); + status.setSortOrder(1); // non-null and >= 0 so the bean isn't treated as a system status + status = Table.insert(getAdmin(), SpecimenSchema.get().getTableInfoSampleRequestStatus(), status); + + SpecimenRequest request = new SpecimenRequest(); + request.setContainer(folder); + request.setStatusId(status.getRowId()); + request = SpecimenRequestManager.get().createRequest(getAdmin(), request, false); + int requestId = request.getRowId(); + + // A plain requester: holds RequestSpecimensPermission (so the action's annotation passes) but is neither a + // coordinator nor the request's owner, so hasEditRequestPermissions() is false. + User requester = createUserInRole(folder, SpecimenRequesterRole.class); + + // Mutating another user's request must be rejected at the per-request guard (403) rather than applied. The + // request id and a no-op status keep the form valid, so without the fix the action proceeds and does NOT 403. + ActionURL url = new ActionURL(ManageRequestStatusAction.class, folder) + .addParameter("id", String.valueOf(requestId)) + .addParameter("status", String.valueOf(status.getRowId())); + assertStatus(HttpServletResponse.SC_FORBIDDEN, post(url, requester)); + + // Positive control: an admin holds ManageRequestsPermission, so the same request passes the guard rather + // than being blocked at 403 -- proving the guard rejects only the unprivileged requester, not every caller. + assertNotEquals("An admin must pass the edit-request guard, not be blocked at 403", + HttpServletResponse.SC_FORBIDDEN, post(url, getAdmin()).getStatus()); + } } } From bd1683ceef992929a1e5b6ecb465a5db6dc2661b Mon Sep 17 00:00:00 2001 From: Karl Lum Date: Wed, 17 Jun 2026 17:29:18 -0700 Subject: [PATCH 484/494] Container scoping for miscellaneous server objects Original PR: https://github.com/LabKey/platform/pull/7767 --- .../specimen/actions/SpecimenController.java | 10 ++-- .../report/SpecimenVisitReportParameters.java | 47 ++++++++++++------- 2 files changed, 38 insertions(+), 19 deletions(-) diff --git a/specimen/src/org/labkey/specimen/actions/SpecimenController.java b/specimen/src/org/labkey/specimen/actions/SpecimenController.java index 059d63a9..1e917b54 100644 --- a/specimen/src/org/labkey/specimen/actions/SpecimenController.java +++ b/specimen/src/org/labkey/specimen/actions/SpecimenController.java @@ -1182,12 +1182,16 @@ public static class SpecimenEventsRedirectAction extends SimpleViewAction Date: Wed, 17 Jun 2026 17:29:25 -0700 Subject: [PATCH 485/494] Container scoping test automation Original PR: https://github.com/LabKey/testAutomation/pull/3054 --- src/org/labkey/test/tests/SpecimenTest.java | 35 ++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/src/org/labkey/test/tests/SpecimenTest.java b/src/org/labkey/test/tests/SpecimenTest.java index 0277fc6e..b91c164d 100644 --- a/src/org/labkey/test/tests/SpecimenTest.java +++ b/src/org/labkey/test/tests/SpecimenTest.java @@ -31,6 +31,7 @@ import org.labkey.test.components.html.BootstrapMenu; import org.labkey.test.pages.ImportDataPage; import org.labkey.test.pages.study.specimen.ManageNotificationsPage; +import org.labkey.test.util.ApiPermissionsHelper; import org.labkey.test.util.DataRegionTable; import org.labkey.test.util.LogMethod; import org.labkey.test.util.LoggedParam; @@ -132,6 +133,7 @@ protected void setupRequestabilityRules() protected void doVerifySteps() throws IOException { verifyActorDetails(); + verifySpecimenEventsRedirect(); createRequest(); verifyViews(); verifyAdditionalRequestFields(); @@ -325,6 +327,37 @@ private void verifyActorDetails() DataRegion(getDriver()).withName("SpecimenRequest").waitFor(); } + // Simulate SpecimenForeignKey redirect behavior + @LogMethod (quiet = true) + private void verifySpecimenEventsRedirect() + { + String targetStudyId = getContainerId(); + + // Create an empty second folder (doesn't need to be a study) with guest read. We'll attempt to invoke the + // redirect action from this folder. + String folderName = "Another Study"; + _containerHelper.createSubfolder(getProjectName(), folderName, "Study"); + new ApiPermissionsHelper(this).setSiteGroupPermissions("Guests", "Reader"); + + // Happy path - admin should redirect + String baseUrl = WebTestHelper.getBaseURL() + "/" + getProjectName() + "/" + folderName + "/specimen-specimenEventsRedirect.view?targetStudy=" + targetStudyId + "&id="; + String url = baseUrl + "AAA07XK5-01"; + beginAt(url); + assertTextPresent("Vial History", "999320812", "350V0600294A"); + + // Guest has access in Another Study but not in the target study (My Study), so should not redirect + signOut(); + beginAt(baseUrl + "abcdefg_123456"); // Bogus ID and user doesn't have read permission + assertTextPresent("Unable to resolve the Specimen ID and target Study"); + beginAt(url); // Valid ID, but user doesn't have read permission to target study, so same error + assertTextPresent("Unable to resolve the Specimen ID and target Study"); + + // Sign in and back to the main study + signIn(); + goToProjectHome(); + clickFolder(getFolderName()); + } + @LogMethod private void createRequest() { @@ -1048,7 +1081,7 @@ private void verifyDrawTimestampConflict(String qcControl, String timestamp, Str { if (StringUtils.isBlank(qcControl)) { - // no conflict, so all three fields shold be valid + // no conflict, so all three fields should be valid assertTrue(StringUtils.isNotBlank(timestamp)); assertTrue(StringUtils.isNotBlank(time)); assertTrue(StringUtils.isNotBlank(date)); From 7e38e875909f3bd1cf6d8ac9de6da8a259dfebf1 Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Tue, 23 Jun 2026 14:39:55 -0700 Subject: [PATCH 486/494] Add some specimen API tests Original PR: https://github.com/LabKey/platform/pull/7787 --- .../labkey/specimen/actions/SpecimenApiController.java | 10 +++++----- .../labkey/specimen/actions/SpecimenController.java | 6 +++++- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/specimen/src/org/labkey/specimen/actions/SpecimenApiController.java b/specimen/src/org/labkey/specimen/actions/SpecimenApiController.java index 6559e4e4..b531e152 100644 --- a/specimen/src/org/labkey/specimen/actions/SpecimenApiController.java +++ b/specimen/src/org/labkey/specimen/actions/SpecimenApiController.java @@ -402,22 +402,22 @@ public void setPreferredLocation(Integer preferredLocation) private SpecimenRequest getRequest(User user, Container container, int rowId, boolean checkOwnership, boolean checkEditability) { SpecimenRequest request = SpecimenRequestManager.get().getRequest(container, rowId); - boolean admin = container.hasPermission(user, RequestSpecimensPermission.class); + boolean admin = container.hasPermission(user, ManageRequestsPermission.class); boolean adminOrOwner = request != null && (admin || request.getCreatedBy() == user.getUserId()); if (request == null || (checkOwnership && !adminOrOwner)) - throw new RuntimeException("Request " + rowId + " was not found or the current user does not have permissions to access it."); + throw new IllegalArgumentException("Request " + rowId + " was not found or the current user does not have permissions to access it."); if (checkEditability) { if (admin) { if (SpecimenRequestManager.get().isInFinalState(request)) - throw new RuntimeException("Request " + rowId + " is in a final state and cannot be modified."); + throw new IllegalArgumentException("Request " + rowId + " is in a final state and cannot be modified."); } else { SpecimenRequestStatus cartStatus = SpecimenRequestManager.get().getRequestShoppingCartStatus(container, user); if (cartStatus == null || request.getStatusId() != cartStatus.getRowId()) - throw new RuntimeException("Request " + rowId + " has been submitted and can only be modified by an administrator."); + throw new IllegalArgumentException("Request " + rowId + " has been submitted and can only be modified by an administrator."); } } return request; @@ -616,7 +616,7 @@ public ApiResponse execute(RequestIdForm deleteRequestForm, BindException errors private void buildTypeSummary(List> summary, List types) { - // Recursively decend through the vial type hierarchy, adding a count property and a list of children for each type. + // Recursively descend through the vial type hierarchy, adding a count property and a list of children for each type. for (SpecimenTypeSummary.TypeCount count : types) { Map countProperties = new TreeMap<>(); diff --git a/specimen/src/org/labkey/specimen/actions/SpecimenController.java b/specimen/src/org/labkey/specimen/actions/SpecimenController.java index b13bdd55..b489354b 100644 --- a/specimen/src/org/labkey/specimen/actions/SpecimenController.java +++ b/specimen/src/org/labkey/specimen/actions/SpecimenController.java @@ -99,6 +99,7 @@ import org.labkey.api.reader.ColumnDescriptor; import org.labkey.api.reader.DataLoader; import org.labkey.api.security.ActionNames; +import org.labkey.api.security.RequiresLogin; import org.labkey.api.security.RequiresPermission; import org.labkey.api.security.User; import org.labkey.api.security.ValidEmail; @@ -1325,12 +1326,12 @@ public void addNavTrail(NavTree root) } @RequiresPermission(ReadPermission.class) + @RequiresLogin public class ViewRequestsAction extends SimpleViewAction { @Override public ModelAndView getView(Object o, BindException errors) { - requiresLogin(); SpecimenRequestQueryView grid = SpecimenRequestQueryView.createView(getViewContext()); grid.setExtraLinks(true); grid.setShowCustomizeLink(false); @@ -4428,6 +4429,9 @@ public class RequestHistoryAction extends SimpleViewAction public ModelAndView getView(IdForm form, BindException errors) { _requestId = form.getId(); + @Nullable SpecimenRequest request = SpecimenRequestManager.get().getRequest(getContainer(), _requestId); + if (null == request) + throw new NotFoundException("Specimen request " + _requestId + " was not found in this study"); HtmlView header = new HtmlView(LinkBuilder.labkeyLink("View Request", SpecimenController.getManageRequestURL(getContainer(), form.getId(), null))); SimpleFilter filter = new SimpleFilter(FieldKey.fromParts("RequestId"), form.getId()); GridView historyGrid = getRequestEventGridView(getViewContext().getRequest(), errors, filter); From df4fff8024875041811edb4d1b9844947cd9a373 Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Tue, 23 Jun 2026 14:40:00 -0700 Subject: [PATCH 487/494] Add some specimen API tests Original PR: https://github.com/LabKey/testAutomation/pull/3068 --- src/org/labkey/test/tests/SpecimenTest.java | 124 +++++++++++++++++++- 1 file changed, 120 insertions(+), 4 deletions(-) diff --git a/src/org/labkey/test/tests/SpecimenTest.java b/src/org/labkey/test/tests/SpecimenTest.java index 2afd5888..bfdad9fd 100644 --- a/src/org/labkey/test/tests/SpecimenTest.java +++ b/src/org/labkey/test/tests/SpecimenTest.java @@ -18,7 +18,13 @@ import org.apache.commons.lang3.StringUtils; import org.apache.hc.core5.http.HttpStatus; +import org.json.JSONObject; +import org.junit.Assert; import org.junit.experimental.categories.Category; +import org.labkey.remoteapi.CommandException; +import org.labkey.remoteapi.CommandResponse; +import org.labkey.remoteapi.Connection; +import org.labkey.remoteapi.SimplePostCommand; import org.labkey.test.BaseWebDriverTest; import org.labkey.test.Locator; import org.labkey.test.Locators; @@ -36,6 +42,7 @@ import org.labkey.test.util.LogMethod; import org.labkey.test.util.LoggedParam; import org.labkey.test.util.PasswordUtil; +import org.labkey.test.util.PermissionsHelper; import org.labkey.test.util.PortalHelper; import org.labkey.test.util.StudyHelper; import org.labkey.test.util.TextSearcher; @@ -51,6 +58,7 @@ import java.util.HashSet; import java.util.Hashtable; import java.util.List; +import java.util.Map; import java.util.Set; import static org.junit.Assert.assertEquals; @@ -59,6 +67,7 @@ import static org.junit.Assert.assertTrue; import static org.labkey.test.pages.study.specimen.ManageNotificationsPage.SpecimensAttachment; import static org.labkey.test.util.DataRegionTable.DataRegion; +import static org.labkey.test.util.PermissionsHelper.READER_ROLE; @Category({Daily.class, Specimen.class}) @BaseWebDriverTest.ClassTimeout(minutes = 20) @@ -124,10 +133,11 @@ protected void setupRequestabilityRules() @Override @LogMethod - protected void doVerifySteps() throws IOException + protected void doVerifySteps() throws IOException, CommandException { verifyActorDetails(); verifySpecimenEventsRedirect(); + createRequestWithApi(); createRequest(); verifyViews(); verifyAdditionalRequestFields(); @@ -322,7 +332,7 @@ private void verifyActorDetails() } // Simulate SpecimenForeignKey redirect behavior - @LogMethod (quiet = true) + @LogMethod private void verifySpecimenEventsRedirect() { String targetStudyId = getContainerId(); @@ -352,6 +362,112 @@ private void verifySpecimenEventsRedirect() clickFolder(getFolderName()); } + @LogMethod + private void createRequestWithApi() throws IOException, CommandException + { + // Create an empty specimen request as admin user + goToSpecimenData(); + click(Locator.tag("img").withAttributeContaining("alt", "[New Request Icon]")); + selectOptionByText(Locator.name("destinationLocation"), DESTINATION_SITE); + setFormElement(Locator.id("input0"), "Assay Plan"); + setFormElement(Locator.id("input1"), "Shipping"); + setFormElement(Locator.id("input3"), "Comments"); + clickButton("Create and View Details"); + int requestId = Integer.parseInt(getUrlParam("id")); + + // Create commands to add vials, remove vials, add specimens, and cancel the request + JSONObject vialParameters = new JSONObject( + Map.of( + "requestId", requestId, + "idType", "GlobalUniqueId", + "vialIds", new String[]{"ABH00LT8-01"} + ) + ); + SimplePostCommand addVialsCommand = new SimplePostCommand("specimen-api", "addVialsToRequest"); + addVialsCommand.setJsonObject(vialParameters); + SimplePostCommand removeVialsCommand = new SimplePostCommand("specimen-api", "removeVialsFromRequest"); + removeVialsCommand.setJsonObject(vialParameters); + SimplePostCommand addSpecimenCommand = new SimplePostCommand("specimen-api", "addSpecimensToRequest"); + addSpecimenCommand.setJsonObject(new JSONObject( + Map.of( + "requestId", requestId, + "specimenHashes", new String[]{"FakeSpecimenHashThatDoesntMatter"} + ) + )); + SimplePostCommand cancelRequestCommand = new SimplePostCommand("specimen-api", "cancelRequest"); + cancelRequestCommand.setJsonObject(new JSONObject( + Map.of( + "requestId", requestId + ) + )); + + // Assign "Specimen Requester" role to USER1 + String containerPath = getCurrentContainerPath(); + log(containerPath); + ApiPermissionsHelper helper = new ApiPermissionsHelper(containerPath); + helper.uncheckInheritedPermissions(); // Uses UI to un-inherit and save... + clickButton("Cancel"); // ...so click button to return to specimen page + helper.addMemberToRole(USER1, READER_ROLE, PermissionsHelper.MemberType.user, containerPath); + helper.addMemberToRole(USER1, "Specimen Requester", PermissionsHelper.MemberType.user, containerPath); + Connection conn = createDefaultConnection(); + + // Impersonate USER1. Specimen Requester who doesn't own the request shouldn't be able to modify it. + impersonate(USER1); + Assert.assertThrows("Request " + requestId + " was not found or the current user does not have permissions to access it.", + CommandException.class, + () -> addVialsCommand.execute(conn, containerPath) + ); + Assert.assertThrows("Request " + requestId + " was not found or the current user does not have permissions to access it.", + CommandException.class, + () -> addSpecimenCommand.execute(conn, containerPath) + ); + Assert.assertThrows("Request " + requestId + " was not found or the current user does not have permissions to access it.", + CommandException.class, + () -> cancelRequestCommand.execute(conn, containerPath) + ); + stopImpersonating(false); + + // Let's have the "Specimen Requester" owner add a vial so USER1 has something to (attempt to) delete + impersonateRoles(READER_ROLE, "Specimen Requester"); + CommandResponse response = addVialsCommand.execute(conn, containerPath); + assertEquals(HttpStatus.SC_OK, response.getStatusCode()); + //noinspection unchecked + assertEquals(1, ((List)response.getParsedData().get("vials")).size()); + stopImpersonating(false); + + // Attempt to remove the just-added vial as "Specimen Requester" non-owner + impersonate(USER1); + Assert.assertThrows("Request " + requestId + " was not found or the current user does not have permissions to access it.", + CommandException.class, + () -> removeVialsCommand.execute(conn, containerPath) + ); + stopImpersonating(false); + + // "Specimen Requester" owner should be able to remove vials and add via specimen hash. This hash won't match + // any vials, but we're just checking permissions. + impersonateRoles(READER_ROLE, "Specimen Requester"); + response = removeVialsCommand.execute(conn, containerPath); + assertEquals(HttpStatus.SC_OK, response.getStatusCode()); + //noinspection unchecked + assertEquals(0, ((List)response.getParsedData().get("vials")).size()); + response = addSpecimenCommand.execute(conn, containerPath); + assertEquals(HttpStatus.SC_OK, response.getStatusCode()); + stopImpersonating(false); + + // Now give USER1 the (more powerful) "Specimen Coordinator" role and verify they can now modify the request + // that they don't own, including canceling it. + helper.addMemberToRole(USER1, "Specimen Coordinator", PermissionsHelper.MemberType.user, containerPath); + impersonate(USER1); + assertEquals(HttpStatus.SC_OK, addVialsCommand.execute(conn, containerPath).getStatusCode()); + assertEquals(HttpStatus.SC_OK, removeVialsCommand.execute(conn, containerPath).getStatusCode()); + assertEquals(HttpStatus.SC_OK, addSpecimenCommand.execute(conn, containerPath).getStatusCode()); + assertEquals(HttpStatus.SC_OK, cancelRequestCommand.execute(conn, containerPath).getStatusCode()); + stopImpersonating(false); + + // Restore permissions back to inherit + helper.checkInheritedPermissions(); + } + @LogMethod private void createRequest() { @@ -584,8 +700,8 @@ private void verifyNotificationEmails() assertTrue(emailMessages1.getFirst().getBody().contains(_specimen_McMichael)); assertNotNull("No message found", emailMessages1); String messageBody = emailMessages2.getFirst().getBody().replaceFirst("-*=_Part_\\d{3}_\\d*.\\d*\\n",""); - messageBody = messageBody.replaceAll("Content-Type: text\\/html; charset=UTF-8\n",""); - messageBody = messageBody.replaceAll("Content-Transfer-Encoding: 7bit\n", ""); + messageBody = messageBody.replace("Content-Type: text/html; charset=UTF-8\n",""); + messageBody = messageBody.replace("Content-Transfer-Encoding: 7bit\n", ""); assertTrue("Notification was not as expected.\nExpected:\n" + notification + "\n\nActual:\n" + messageBody, messageBody.contains(notification)); String attachment1 = getAttribute(Locator.linkWithText(ATTACHMENT1), "href"); From 16205a3348735cfbccbae17e1b5da543222bb183 Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Wed, 29 Jul 2026 16:24:53 -0700 Subject: [PATCH 488/494] Minor clean ups Original PR: https://github.com/LabKey/platform/pull/7888 ## Rationale - Clean up some warnings and unused methods in `JspBase` - Migrate uses of Commons Collections `Bag` to `MultiSet`. MultiSet is the replacement for Bag that's fully compatible with the Collection contract. Note: Originally this was meant to update our CSPs to use report-to exclusively (https://github.com/LabKey/internal-issues/issues/900), but we'll revisit that after confirming support for this directive on TeamCity. --- .../org/labkey/specimen/SpecimenModule.java | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/specimen/src/org/labkey/specimen/SpecimenModule.java b/specimen/src/org/labkey/specimen/SpecimenModule.java index e5acfced..16fa4834 100644 --- a/specimen/src/org/labkey/specimen/SpecimenModule.java +++ b/specimen/src/org/labkey/specimen/SpecimenModule.java @@ -16,7 +16,8 @@ package org.labkey.specimen; -import org.apache.commons.collections4.bag.HashBag; +import org.apache.commons.collections4.MultiSet; +import org.apache.commons.collections4.multiset.HashMultiSet; import org.apache.commons.lang3.mutable.MutableInt; import org.apache.logging.log4j.Logger; import org.jetbrains.annotations.NotNull; @@ -74,12 +75,12 @@ import org.labkey.specimen.importer.SpecimenSettingsImporter; import org.labkey.specimen.model.SpecimenRequestEventType; import org.labkey.specimen.pipeline.SpecimenPipeline; -import org.labkey.specimen.requirements.SpecimenRequestRequirementProvider; import org.labkey.specimen.query.SpecimenPivotByDerivativeType; import org.labkey.specimen.query.SpecimenPivotByPrimaryType; import org.labkey.specimen.query.SpecimenPivotByRequestingLocation; import org.labkey.specimen.query.SpecimenQueryView; import org.labkey.specimen.query.SpecimenUpdateService; +import org.labkey.specimen.requirements.SpecimenRequestRequirementProvider; import org.labkey.specimen.security.roles.SpecimenCoordinatorRole; import org.labkey.specimen.security.roles.SpecimenRequesterRole; import org.labkey.specimen.settings.RepositorySettings; @@ -272,7 +273,7 @@ protected void startupAfterSpringConfig(ModuleContext moduleContext) { svc.registerUsageMetrics(NAME, () -> { // Collect and add specimen repository statistics: simple vs. advanced study count, event/vial/specimen count, count of studies with requests enabled, request count by status - HashBag specimenBag = new HashBag<>(); + MultiSet specimenMultiSet = new HashMultiSet<>(); MutableInt requestsEnabled = new MutableInt(0); MutableInt hasLocations = new MutableInt(0); @@ -283,19 +284,19 @@ protected void startupAfterSpringConfig(ModuleContext moduleContext) if (settings.isSimple()) { - specimenBag.add("simple"); + specimenMultiSet.add("simple"); TableInfo simpleSpecimens = schema.getTable(SpecimenTablesProvider.SIMPLE_SPECIMEN_TABLE_NAME); - specimenBag.add("simpleSpecimens", (int) new TableSelector(simpleSpecimens).getRowCount()); + specimenMultiSet.add("simpleSpecimens", (int) new TableSelector(simpleSpecimens).getRowCount()); } else { - specimenBag.add("advanced"); + specimenMultiSet.add("advanced"); TableInfo events = schema.getTable(SpecimenTablesProvider.SPECIMEN_EVENT_TABLE_NAME); TableInfo vials = schema.getTable(SpecimenTablesProvider.SPECIMEN_DETAIL_TABLE_NAME); TableInfo specimens = schema.getTable(SpecimenTablesProvider.SPECIMEN_SUMMARY_TABLE_NAME); - specimenBag.add("events", (int) new TableSelector(events).getRowCount()); - specimenBag.add("vials", (int) new TableSelector(vials).getRowCount()); - specimenBag.add("specimens", (int) new TableSelector(specimens).getRowCount()); + specimenMultiSet.add("events", (int) new TableSelector(events).getRowCount()); + specimenMultiSet.add("vials", (int) new TableSelector(vials).getRowCount()); + specimenMultiSet.add("specimens", (int) new TableSelector(specimens).getRowCount()); } if (settings.isEnableRequests()) @@ -303,15 +304,15 @@ protected void startupAfterSpringConfig(ModuleContext moduleContext) TableInfo locations = schema.getTable(SpecimenQuerySchema.LOCATION_TABLE_NAME); long locationCount = new TableSelector(locations).getRowCount(); - specimenBag.add("locations", (int) locationCount); - specimenBag.add("locationsInUse", (int) new TableSelector(locations, new SimpleFilter(FieldKey.fromParts("In Use"), true), null).getRowCount()); + specimenMultiSet.add("locations", (int) locationCount); + specimenMultiSet.add("locationsInUse", (int) new TableSelector(locations, new SimpleFilter(FieldKey.fromParts("In Use"), true), null).getRowCount()); if (locationCount > 0) hasLocations.increment(); - LOG.debug(specimenBag.toString()); + LOG.debug(specimenMultiSet.toString()); }); - Map specimensMap = specimenBag.uniqueSet().stream().collect(Collectors.toMap(s -> s, specimenBag::getCount)); + Map specimensMap = specimenMultiSet.uniqueSet().stream().collect(Collectors.toMap(s -> s, specimenMultiSet::getCount)); Map requestsMap = new SqlSelector(SpecimenSchema.get().getSchema(), new SQLFragment("SELECT Label, COUNT(*) FROM study.SampleRequest INNER JOIN study.SampleRequestStatus srs ON StatusId = srs.RowId GROUP BY Label")).getValueMap(String.class); requestsMap.put("enabled", requestsEnabled); specimensMap.put("requests", requestsMap); From e3b01ca9f4cdbf183ebd98b7f6db61c8e9850c34 Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Wed, 29 Jul 2026 21:41:06 -0700 Subject: [PATCH 489/494] Move specimen permissions into specimen module Original PR: https://github.com/LabKey/platform/pull/7889 ## Rationale Move a few classes from study into specimen. Also, on the study summary page, stop displaying a "Manage Study" link to Specimen Coordinators when requests are disabled. This avoids leading them to a Manage Study page that's completely blank. #### Tasks - [x] Dev - [x] Claude code review - [x] Code review - [x] Manual testing - automated testing should be sufficient - [x] TeamCity review and merge --- .../org/labkey/specimen/SpecimenModule.java | 28 +++++++++++++++++ .../specimen/SpecimenRequestManager.java | 2 +- .../actions/SpecimenApiController.java | 4 +-- .../specimen/actions/SpecimenController.java | 11 +++---- .../specimen/query/SpecimenQueryView.java | 4 +-- .../query/SpecimenRequestQueryView.java | 2 +- .../EditSpecimenDataPermission.java | 31 +++++++++++++++++++ .../ManageRequestSettingsPermission.java | 31 +++++++++++++++++++ .../RequestSpecimensPermission.java | 31 +++++++++++++++++++ .../roles/SpecimenCoordinatorRole.java | 6 ++-- .../security/roles/SpecimenRequesterRole.java | 2 +- .../view/SpecimenToolsWebPartFactory.java | 2 +- .../labkey/specimen/view/manageSpecimens.jsp | 4 +-- .../src/org/labkey/specimen/view/webPart.jsp | 8 ++--- 14 files changed, 143 insertions(+), 23 deletions(-) create mode 100644 specimen/src/org/labkey/specimen/security/permissions/EditSpecimenDataPermission.java create mode 100644 specimen/src/org/labkey/specimen/security/permissions/ManageRequestSettingsPermission.java create mode 100644 specimen/src/org/labkey/specimen/security/permissions/RequestSpecimensPermission.java diff --git a/specimen/src/org/labkey/specimen/SpecimenModule.java b/specimen/src/org/labkey/specimen/SpecimenModule.java index 16fa4834..212e54d1 100644 --- a/specimen/src/org/labkey/specimen/SpecimenModule.java +++ b/specimen/src/org/labkey/specimen/SpecimenModule.java @@ -43,6 +43,7 @@ import org.labkey.api.query.QueryView; import org.labkey.api.query.UserSchema; import org.labkey.api.security.User; +import org.labkey.api.security.permissions.Permission; import org.labkey.api.security.roles.RoleManager; import org.labkey.api.specimen.SpecimenMigrationService; import org.labkey.api.specimen.SpecimenQuerySchema; @@ -53,10 +54,14 @@ import org.labkey.api.study.Study; import org.labkey.api.study.StudyInternalService; import org.labkey.api.study.StudyService; +import org.labkey.api.study.StudyUrls; import org.labkey.api.study.importer.SimpleStudyImportContext; import org.labkey.api.study.importer.SimpleStudyImporterRegistry; import org.labkey.api.study.writer.SimpleStudyWriterRegistry; import org.labkey.api.usageMetrics.UsageMetricsService; +import org.labkey.api.util.LinkBuilder; +import org.labkey.api.util.PageFlowUtil; +import org.labkey.api.util.SafeToRender; import org.labkey.api.util.SystemMaintenance; import org.labkey.api.util.emailTemplate.EmailTemplateService; import org.labkey.api.util.logging.LogHelper; @@ -81,6 +86,8 @@ import org.labkey.specimen.query.SpecimenQueryView; import org.labkey.specimen.query.SpecimenUpdateService; import org.labkey.specimen.requirements.SpecimenRequestRequirementProvider; +import org.labkey.specimen.security.permissions.EditSpecimenDataPermission; +import org.labkey.specimen.security.permissions.ManageRequestSettingsPermission; import org.labkey.specimen.security.roles.SpecimenCoordinatorRole; import org.labkey.specimen.security.roles.SpecimenRequesterRole; import org.labkey.specimen.settings.RepositorySettings; @@ -235,6 +242,27 @@ public void addSpecimenPivotTableNames(Set names) } return null; } + + @Override + public @Nullable SafeToRender getSpecimenSettingsLink(Container c, User user) + { + // Must have ManageRequestSettingsPermission and requests enabled (otherwise non-admin will see a blank Manage Study page) + if (c.hasPermission(user, ManageRequestSettingsPermission.class) && isEnableRequests(c)) + { + return LinkBuilder.labkeyLink( + "Manage Specimen Request Settings", + PageFlowUtil.urlProvider(StudyUrls.class).getManageStudyURL(c) + ); + } + + return null; + } + + @Override + public @NotNull Class getEditSpecimenDataPermission() + { + return EditSpecimenDataPermission.class; + } }); SystemMaintenance.addTask(new QueryBasedSpecimenImportUploadTask()); diff --git a/specimen/src/org/labkey/specimen/SpecimenRequestManager.java b/specimen/src/org/labkey/specimen/SpecimenRequestManager.java index 5107f286..0e601503 100644 --- a/specimen/src/org/labkey/specimen/SpecimenRequestManager.java +++ b/specimen/src/org/labkey/specimen/SpecimenRequestManager.java @@ -56,7 +56,6 @@ import org.labkey.api.specimen.importer.RollupInstance; import org.labkey.api.specimen.importer.VialSpecimenRollup; import org.labkey.api.specimen.model.SpecimenTablesProvider; -import org.labkey.api.specimen.security.permissions.RequestSpecimensPermission; import org.labkey.api.study.QueryHelper; import org.labkey.api.study.QueryHelper.StudyCacheCollections; import org.labkey.api.study.SpecimenUrls; @@ -74,6 +73,7 @@ import org.labkey.specimen.requirements.SpecimenRequestRequirement; import org.labkey.specimen.requirements.SpecimenRequestRequirementProvider; import org.labkey.specimen.security.permissions.ManageRequestsPermission; +import org.labkey.specimen.security.permissions.RequestSpecimensPermission; import org.labkey.specimen.settings.SettingsManager; import java.io.IOException; diff --git a/specimen/src/org/labkey/specimen/actions/SpecimenApiController.java b/specimen/src/org/labkey/specimen/actions/SpecimenApiController.java index b531e152..122ec311 100644 --- a/specimen/src/org/labkey/specimen/actions/SpecimenApiController.java +++ b/specimen/src/org/labkey/specimen/actions/SpecimenApiController.java @@ -33,8 +33,6 @@ import org.labkey.api.specimen.Vial; import org.labkey.api.specimen.location.LocationImpl; import org.labkey.api.specimen.location.LocationManager; -import org.labkey.specimen.model.SpecimenTypeSummary; -import org.labkey.api.specimen.security.permissions.RequestSpecimensPermission; import org.labkey.api.study.StudyUtils; import org.labkey.api.util.PageFlowUtil; import org.labkey.api.view.NotFoundException; @@ -48,8 +46,10 @@ import org.labkey.specimen.model.AdditiveType; import org.labkey.specimen.model.DerivativeType; import org.labkey.specimen.model.PrimaryType; +import org.labkey.specimen.model.SpecimenTypeSummary; import org.labkey.specimen.requirements.SpecimenRequest; import org.labkey.specimen.security.permissions.ManageRequestsPermission; +import org.labkey.specimen.security.permissions.RequestSpecimensPermission; import org.labkey.specimen.settings.RepositorySettings; import org.labkey.specimen.settings.SettingsManager; import org.springframework.validation.BindException; diff --git a/specimen/src/org/labkey/specimen/actions/SpecimenController.java b/specimen/src/org/labkey/specimen/actions/SpecimenController.java index b489354b..f46bc978 100644 --- a/specimen/src/org/labkey/specimen/actions/SpecimenController.java +++ b/specimen/src/org/labkey/specimen/actions/SpecimenController.java @@ -107,7 +107,6 @@ import org.labkey.api.security.permissions.AdminPermission; import org.labkey.api.security.permissions.ReadPermission; import org.labkey.api.security.permissions.UpdatePermission; -import org.labkey.api.security.roles.ReaderRole; import org.labkey.api.specimen.SpecimenQuerySchema; import org.labkey.api.specimen.SpecimenSchema; import org.labkey.api.specimen.Vial; @@ -115,9 +114,6 @@ import org.labkey.api.specimen.location.LocationManager; import org.labkey.api.specimen.model.SpecimenComment; import org.labkey.api.specimen.model.SpecimenTablesProvider; -import org.labkey.api.specimen.security.permissions.EditSpecimenDataPermission; -import org.labkey.api.specimen.security.permissions.ManageRequestSettingsPermission; -import org.labkey.api.specimen.security.permissions.RequestSpecimensPermission; import org.labkey.api.study.CohortFilter; import org.labkey.api.study.Dataset; import org.labkey.api.study.MapArrayExcelWriter; @@ -171,11 +167,10 @@ import org.labkey.specimen.RequestEventType; import org.labkey.specimen.RequestedSpecimens; import org.labkey.specimen.SpecimenManager; -import org.labkey.specimen.SpecimenRequestException; import org.labkey.specimen.SpecimenModule; +import org.labkey.specimen.SpecimenRequestException; import org.labkey.specimen.SpecimenRequestManager; import org.labkey.specimen.SpecimenRequestStatus; -import org.labkey.specimen.security.roles.SpecimenRequesterRole; import org.labkey.specimen.importer.QueryBasedSpecimenTransform; import org.labkey.specimen.importer.RequestabilityManager; import org.labkey.specimen.importer.SimpleSpecimenImporter; @@ -194,14 +189,18 @@ import org.labkey.specimen.requirements.SpecimenRequestRequirement; import org.labkey.specimen.requirements.SpecimenRequestRequirementProvider; import org.labkey.specimen.requirements.SpecimenRequestRequirementType; +import org.labkey.specimen.security.permissions.EditSpecimenDataPermission; import org.labkey.specimen.security.permissions.ManageDisplaySettingsPermission; import org.labkey.specimen.security.permissions.ManageNewRequestFormPermission; import org.labkey.specimen.security.permissions.ManageNotificationsPermission; import org.labkey.specimen.security.permissions.ManageRequestRequirementsPermission; +import org.labkey.specimen.security.permissions.ManageRequestSettingsPermission; import org.labkey.specimen.security.permissions.ManageRequestStatusesPermission; import org.labkey.specimen.security.permissions.ManageRequestsPermission; import org.labkey.specimen.security.permissions.ManageSpecimenActorsPermission; +import org.labkey.specimen.security.permissions.RequestSpecimensPermission; import org.labkey.specimen.security.permissions.SetSpecimenCommentsPermission; +import org.labkey.specimen.security.roles.SpecimenRequesterRole; import org.labkey.specimen.settings.DisplaySettings; import org.labkey.specimen.settings.RepositorySettings; import org.labkey.specimen.settings.RequestNotificationSettings; diff --git a/specimen/src/org/labkey/specimen/query/SpecimenQueryView.java b/specimen/src/org/labkey/specimen/query/SpecimenQueryView.java index e8970fda..8e23a2b5 100644 --- a/specimen/src/org/labkey/specimen/query/SpecimenQueryView.java +++ b/specimen/src/org/labkey/specimen/query/SpecimenQueryView.java @@ -50,8 +50,6 @@ import org.labkey.api.specimen.SpecimenSchema; import org.labkey.api.specimen.Vial; import org.labkey.api.specimen.model.SpecimenTablesProvider; -import org.labkey.api.specimen.security.permissions.EditSpecimenDataPermission; -import org.labkey.api.specimen.security.permissions.RequestSpecimensPermission; import org.labkey.api.study.CohortFilter; import org.labkey.api.study.Study; import org.labkey.api.study.StudyService; @@ -66,6 +64,8 @@ import org.labkey.api.view.ViewContext; import org.labkey.api.writer.HtmlWriter; import org.labkey.specimen.actions.SpecimenController; +import org.labkey.specimen.security.permissions.EditSpecimenDataPermission; +import org.labkey.specimen.security.permissions.RequestSpecimensPermission; import org.labkey.specimen.settings.DisplaySettings; import org.labkey.specimen.settings.RepositorySettings; import org.labkey.specimen.settings.SettingsManager; diff --git a/specimen/src/org/labkey/specimen/query/SpecimenRequestQueryView.java b/specimen/src/org/labkey/specimen/query/SpecimenRequestQueryView.java index cef34526..851d0c9b 100644 --- a/specimen/src/org/labkey/specimen/query/SpecimenRequestQueryView.java +++ b/specimen/src/org/labkey/specimen/query/SpecimenRequestQueryView.java @@ -29,7 +29,6 @@ import org.labkey.api.query.UserSchema; import org.labkey.api.security.User; import org.labkey.api.specimen.SpecimenQuerySchema; -import org.labkey.api.specimen.security.permissions.RequestSpecimensPermission; import org.labkey.api.study.Study; import org.labkey.api.study.StudyService; import org.labkey.api.study.StudyUtils; @@ -44,6 +43,7 @@ import org.labkey.specimen.actions.SpecimenController.ManageRequestAction; import org.labkey.specimen.actions.SpecimenController.SubmitRequestAction; import org.labkey.specimen.security.permissions.ManageRequestsPermission; +import org.labkey.specimen.security.permissions.RequestSpecimensPermission; import org.labkey.specimen.settings.SettingsManager; import java.util.Set; diff --git a/specimen/src/org/labkey/specimen/security/permissions/EditSpecimenDataPermission.java b/specimen/src/org/labkey/specimen/security/permissions/EditSpecimenDataPermission.java new file mode 100644 index 00000000..cf6ce732 --- /dev/null +++ b/specimen/src/org/labkey/specimen/security/permissions/EditSpecimenDataPermission.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2013-2026 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.specimen.security.permissions; + +import org.labkey.api.security.permissions.AbstractPermission; +import org.labkey.specimen.SpecimenModule; + +public class EditSpecimenDataPermission extends AbstractPermission +{ + public EditSpecimenDataPermission() + { + super( + "Edit Specimen Data", + "Allows editing of specimen data", + SpecimenModule.class + ); + } +} diff --git a/specimen/src/org/labkey/specimen/security/permissions/ManageRequestSettingsPermission.java b/specimen/src/org/labkey/specimen/security/permissions/ManageRequestSettingsPermission.java new file mode 100644 index 00000000..7354ae53 --- /dev/null +++ b/specimen/src/org/labkey/specimen/security/permissions/ManageRequestSettingsPermission.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2009-2026 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.specimen.security.permissions; + +import org.labkey.api.security.permissions.AbstractPermission; +import org.labkey.specimen.SpecimenModule; + +public class ManageRequestSettingsPermission extends AbstractPermission +{ + public ManageRequestSettingsPermission() + { + super( + "Manage Specimen Request and Tracking Settings", + "Allows management of specimen request and tracking settings", + SpecimenModule.class + ); + } +} \ No newline at end of file diff --git a/specimen/src/org/labkey/specimen/security/permissions/RequestSpecimensPermission.java b/specimen/src/org/labkey/specimen/security/permissions/RequestSpecimensPermission.java new file mode 100644 index 00000000..3e34fcf3 --- /dev/null +++ b/specimen/src/org/labkey/specimen/security/permissions/RequestSpecimensPermission.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2009-2026 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.specimen.security.permissions; + +import org.labkey.api.security.permissions.AbstractPermission; +import org.labkey.specimen.SpecimenModule; + +public class RequestSpecimensPermission extends AbstractPermission +{ + public RequestSpecimensPermission() + { + super( + "Request Specimens", + "Allows request of specimens", + SpecimenModule.class + ); + } +} \ No newline at end of file diff --git a/specimen/src/org/labkey/specimen/security/roles/SpecimenCoordinatorRole.java b/specimen/src/org/labkey/specimen/security/roles/SpecimenCoordinatorRole.java index 1c2d4850..77f1b495 100644 --- a/specimen/src/org/labkey/specimen/security/roles/SpecimenCoordinatorRole.java +++ b/specimen/src/org/labkey/specimen/security/roles/SpecimenCoordinatorRole.java @@ -17,18 +17,18 @@ import org.jetbrains.annotations.NotNull; import org.labkey.api.security.permissions.EditSharedViewPermission; -import org.labkey.api.specimen.security.permissions.EditSpecimenDataPermission; -import org.labkey.api.specimen.security.permissions.ManageRequestSettingsPermission; -import org.labkey.api.specimen.security.permissions.RequestSpecimensPermission; import org.labkey.api.study.security.permissions.ManageStudyPermission; +import org.labkey.specimen.security.permissions.EditSpecimenDataPermission; import org.labkey.specimen.security.permissions.LockSpecimensPermission; import org.labkey.specimen.security.permissions.ManageDisplaySettingsPermission; import org.labkey.specimen.security.permissions.ManageNewRequestFormPermission; import org.labkey.specimen.security.permissions.ManageNotificationsPermission; import org.labkey.specimen.security.permissions.ManageRequestRequirementsPermission; +import org.labkey.specimen.security.permissions.ManageRequestSettingsPermission; import org.labkey.specimen.security.permissions.ManageRequestStatusesPermission; import org.labkey.specimen.security.permissions.ManageRequestsPermission; import org.labkey.specimen.security.permissions.ManageSpecimenActorsPermission; +import org.labkey.specimen.security.permissions.RequestSpecimensPermission; import org.labkey.specimen.security.permissions.SetSpecimenCommentsPermission; import java.util.Collection; diff --git a/specimen/src/org/labkey/specimen/security/roles/SpecimenRequesterRole.java b/specimen/src/org/labkey/specimen/security/roles/SpecimenRequesterRole.java index c469da2d..6f6f5d4f 100644 --- a/specimen/src/org/labkey/specimen/security/roles/SpecimenRequesterRole.java +++ b/specimen/src/org/labkey/specimen/security/roles/SpecimenRequesterRole.java @@ -16,7 +16,7 @@ package org.labkey.specimen.security.roles; import org.jetbrains.annotations.NotNull; -import org.labkey.api.specimen.security.permissions.RequestSpecimensPermission; +import org.labkey.specimen.security.permissions.RequestSpecimensPermission; import java.util.Collection; import java.util.Set; diff --git a/specimen/src/org/labkey/specimen/view/SpecimenToolsWebPartFactory.java b/specimen/src/org/labkey/specimen/view/SpecimenToolsWebPartFactory.java index bed997ca..a0b3a28a 100644 --- a/specimen/src/org/labkey/specimen/view/SpecimenToolsWebPartFactory.java +++ b/specimen/src/org/labkey/specimen/view/SpecimenToolsWebPartFactory.java @@ -15,7 +15,6 @@ */ package org.labkey.specimen.view; -import org.labkey.api.specimen.security.permissions.RequestSpecimensPermission; import org.labkey.api.study.StudyUrls; import org.labkey.api.study.security.permissions.ManageStudyPermission; import org.labkey.api.study.view.StudyToolsWebPart; @@ -27,6 +26,7 @@ import org.labkey.specimen.actions.ShowSearchAction; import org.labkey.specimen.actions.SpecimenController.AutoReportListAction; import org.labkey.specimen.actions.SpecimenController.ShowCreateSpecimenRequestAction; +import org.labkey.specimen.security.permissions.RequestSpecimensPermission; import org.labkey.specimen.settings.SettingsManager; import java.util.ArrayList; diff --git a/specimen/src/org/labkey/specimen/view/manageSpecimens.jsp b/specimen/src/org/labkey/specimen/view/manageSpecimens.jsp index d84696c2..278fab27 100644 --- a/specimen/src/org/labkey/specimen/view/manageSpecimens.jsp +++ b/specimen/src/org/labkey/specimen/view/manageSpecimens.jsp @@ -15,13 +15,12 @@ * limitations under the License. */ %> -<%@ page import="com.google.common.collect.Iterables"%> +<%@ page import="com.google.common.collect.Iterables" %> <%@ page import="org.labkey.api.data.Container" %> <%@ page import="org.labkey.api.exp.property.Domain" %> <%@ page import="org.labkey.api.security.User" %> <%@ page import="org.labkey.api.security.permissions.AdminPermission" %> <%@ page import="org.labkey.api.specimen.model.SpecimenTablesProvider" %> -<%@ page import="org.labkey.api.specimen.security.permissions.ManageRequestSettingsPermission" %> <%@ page import="org.labkey.api.study.SpecimenService" %> <%@ page import="org.labkey.api.study.SpecimenTransform" %> <%@ page import="org.labkey.api.study.Study" %> @@ -41,6 +40,7 @@ <%@ page import="org.labkey.specimen.actions.SpecimenController.ManageSpecimenWebPartAction" %> <%@ page import="org.labkey.specimen.actions.SpecimenController.ManageStatusesAction" %> <%@ page import="org.labkey.specimen.requirements.SpecimenRequestRequirementProvider" %> +<%@ page import="org.labkey.specimen.security.permissions.ManageRequestSettingsPermission" %> <%@ page import="org.labkey.specimen.settings.SettingsManager" %> <%@ page import="java.util.Collection" %> <%@ page extends="org.labkey.api.jsp.JspBase" %> diff --git a/specimen/src/org/labkey/specimen/view/webPart.jsp b/specimen/src/org/labkey/specimen/view/webPart.jsp index e88ba99f..8f2fdaf5 100644 --- a/specimen/src/org/labkey/specimen/view/webPart.jsp +++ b/specimen/src/org/labkey/specimen/view/webPart.jsp @@ -15,10 +15,9 @@ * 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.AdminPermission"%> -<%@ page import="org.labkey.api.specimen.security.permissions.RequestSpecimensPermission"%> +<%@ page import="org.labkey.api.data.Container" %> +<%@ page import="org.labkey.api.security.User" %> +<%@ page import="org.labkey.api.security.permissions.AdminPermission" %> <%@ page import="org.labkey.api.study.StudyUrls" %> <%@ page import="org.labkey.api.util.HtmlString" %> <%@ page import="org.labkey.api.view.ActionURL" %> @@ -29,6 +28,7 @@ <%@ page import="org.labkey.specimen.actions.SpecimenController.AutoReportListAction" %> <%@ page import="org.labkey.specimen.actions.SpecimenController.ShowCreateSpecimenRequestAction" %> <%@ page import="org.labkey.specimen.actions.SpecimenController.ViewRequestsAction" %> +<%@ page import="org.labkey.specimen.security.permissions.RequestSpecimensPermission" %> <%@ page import="org.labkey.specimen.settings.SettingsManager" %> <%@ page import="org.labkey.specimen.view.SpecimenWebPart" %> <%@ page extends="org.labkey.api.jsp.JspBase" %> From ee4f3f6d7420921445a1dda756c3370b428ff068 Mon Sep 17 00:00:00 2001 From: Josh Eckels Date: Sun, 23 Aug 2026 08:58:39 -0700 Subject: [PATCH 490/494] Remove SQL Server upgrade scripts and code Original PR: https://github.com/LabKey/platform/pull/7969 ## Rationale We no longer support SQLServer as a primary DB ## Changes - Remove schema upgrade scripts - Remove SQLServer conditional code - Remove `SupportedDatabases` in `module.properties` - Remove uses of `PostgresOnlyTest` --- specimen/module.properties | 1 - .../specimen/importer/SpecimenImporter.java | 25 ++++++------------- 2 files changed, 7 insertions(+), 19 deletions(-) diff --git a/specimen/module.properties b/specimen/module.properties index 465594d3..b069d0b2 100644 --- a/specimen/module.properties +++ b/specimen/module.properties @@ -5,5 +5,4 @@ Description: A system for tracking specimens as part of a study. \ URL: https://www.labkey.com/products-services/sample-management-software/ License: Apache 2.0 LicenseURL: http://www.apache.org/licenses/LICENSE-2.0 -SupportedDatabases: mssql, pgsql ManageVersion: true diff --git a/specimen/src/org/labkey/specimen/importer/SpecimenImporter.java b/specimen/src/org/labkey/specimen/importer/SpecimenImporter.java index 41d186ad..24d0d6fd 100644 --- a/specimen/src/org/labkey/specimen/importer/SpecimenImporter.java +++ b/specimen/src/org/labkey/specimen/importer/SpecimenImporter.java @@ -485,25 +485,14 @@ private void populateSpecimenTables(SpecimenLoadInfo info, boolean merge) throws { // SimpleFilter containerFilter = SimpleFilter.createContainerFilter(info.getContainer()); info("Deleting old data from SpecimenEvent, Vial and Specimen tables..."); - if (getTableInfoSpecimen().getSchema().getSqlDialect().isPostgreSQL()) - { - SQLFragment sql = new SQLFragment("TRUNCATE ") - .append(getTableInfoSpecimenEvent().getSelectName()) - .append(", ") - .append(getTableInfoVial().getSelectName()) - .append(", ") - .append(getTableInfoSpecimen().getSelectName()); + SQLFragment sql = new SQLFragment("TRUNCATE ") + .append(getTableInfoSpecimenEvent().getSelectName()) + .append(", ") + .append(getTableInfoVial().getSelectName()) + .append(", ") + .append(getTableInfoSpecimen().getSelectName()); - executeSQL(getTableInfoSpecimen().getSchema(), sql); - } - else - { - Table.delete(getTableInfoSpecimenEvent()); - ensureNotCanceled(); - Table.delete(getTableInfoVial()); - ensureNotCanceled(); - Table.delete(getTableInfoSpecimen()); - } + executeSQL(getTableInfoSpecimen().getSchema(), sql); info("Complete."); } From e9df4fb4f45c73a36efca5168259a65502531b68 Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Fri, 25 Sep 2026 12:06:28 -0700 Subject: [PATCH 491/494] Pass containers into ExperimentService methods Original PR: https://github.com/LabKey/platform/pull/8073 ## Rationale Push containers into `ExperimentService` retrieval methods for performance and reliability. https://github.com/LabKey/kanban/issues/1969 --- .../requirements/DefaultRequirementProvider.java | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/specimen/src/org/labkey/specimen/requirements/DefaultRequirementProvider.java b/specimen/src/org/labkey/specimen/requirements/DefaultRequirementProvider.java index 3afa007a..2a793175 100644 --- a/specimen/src/org/labkey/specimen/requirements/DefaultRequirementProvider.java +++ b/specimen/src/org/labkey/specimen/requirements/DefaultRequirementProvider.java @@ -100,11 +100,7 @@ private synchronized String getDefaultRequirementPlaceholder(final Container con @Override public R getRequirement(Container container, Object requirementPrimaryKey) { - R requirement = new TableSelector(getRequirementTableInfo()).getObject(requirementPrimaryKey, _requirementClass); - // The lookup is by global primary key; reject rows that don't belong to the requested container - if (requirement != null && !container.equals(requirement.getContainer())) - return null; - return requirement; + return new TableSelector(getRequirementTableInfo()).getObject(container, requirementPrimaryKey, _requirementClass); } public R[] getRequirements(Container container, String ownerEntityId) @@ -137,11 +133,7 @@ public A[] getActors(Container c) @Override public A getActor(Container c, Object primaryKey) { - A actor = new TableSelector(getActorTableInfo()).getObject(primaryKey, _actorClass); - // The lookup is by global primary key; reject rows that don't belong to the requested container - if (actor != null && !c.equals(actor.getContainer())) - return null; - return actor; + return new TableSelector(getActorTableInfo()).getObject(c, primaryKey, _actorClass); } @Override From 41e2629a682c319f2e9b86fc3336af01ff320f71 Mon Sep 17 00:00:00 2001 From: labkey-tchad Date: Fri, 25 Sep 2026 14:09:00 -0700 Subject: [PATCH 492/494] Move specimen tests to module test tree --- {src => specimen/test/src}/org/labkey/test/tests/AliquotTest.java | 0 .../src}/org/labkey/test/tests/CustomizeEmailTemplateTest.java | 0 .../test/src}/org/labkey/test/tests/SpecimenBaseTest.java | 0 .../test/src}/org/labkey/test/tests/SpecimenCustomizeTest.java | 0 .../test/src}/org/labkey/test/tests/SpecimenExportTest.java | 0 .../test/src}/org/labkey/test/tests/SpecimenGridExportTest.java | 0 .../test/src}/org/labkey/test/tests/SpecimenImportTest.java | 0 .../src}/org/labkey/test/tests/SpecimenMultipleImportTest.java | 0 .../test/src}/org/labkey/test/tests/SpecimenTest.java | 0 9 files changed, 0 insertions(+), 0 deletions(-) rename {src => specimen/test/src}/org/labkey/test/tests/AliquotTest.java (100%) rename {src => specimen/test/src}/org/labkey/test/tests/CustomizeEmailTemplateTest.java (100%) rename {src => specimen/test/src}/org/labkey/test/tests/SpecimenBaseTest.java (100%) rename {src => specimen/test/src}/org/labkey/test/tests/SpecimenCustomizeTest.java (100%) rename {src => specimen/test/src}/org/labkey/test/tests/SpecimenExportTest.java (100%) rename {src => specimen/test/src}/org/labkey/test/tests/SpecimenGridExportTest.java (100%) rename {src => specimen/test/src}/org/labkey/test/tests/SpecimenImportTest.java (100%) rename {src => specimen/test/src}/org/labkey/test/tests/SpecimenMultipleImportTest.java (100%) rename {src => specimen/test/src}/org/labkey/test/tests/SpecimenTest.java (100%) diff --git a/src/org/labkey/test/tests/AliquotTest.java b/specimen/test/src/org/labkey/test/tests/AliquotTest.java similarity index 100% rename from src/org/labkey/test/tests/AliquotTest.java rename to specimen/test/src/org/labkey/test/tests/AliquotTest.java diff --git a/src/org/labkey/test/tests/CustomizeEmailTemplateTest.java b/specimen/test/src/org/labkey/test/tests/CustomizeEmailTemplateTest.java similarity index 100% rename from src/org/labkey/test/tests/CustomizeEmailTemplateTest.java rename to specimen/test/src/org/labkey/test/tests/CustomizeEmailTemplateTest.java diff --git a/src/org/labkey/test/tests/SpecimenBaseTest.java b/specimen/test/src/org/labkey/test/tests/SpecimenBaseTest.java similarity index 100% rename from src/org/labkey/test/tests/SpecimenBaseTest.java rename to specimen/test/src/org/labkey/test/tests/SpecimenBaseTest.java diff --git a/src/org/labkey/test/tests/SpecimenCustomizeTest.java b/specimen/test/src/org/labkey/test/tests/SpecimenCustomizeTest.java similarity index 100% rename from src/org/labkey/test/tests/SpecimenCustomizeTest.java rename to specimen/test/src/org/labkey/test/tests/SpecimenCustomizeTest.java diff --git a/src/org/labkey/test/tests/SpecimenExportTest.java b/specimen/test/src/org/labkey/test/tests/SpecimenExportTest.java similarity index 100% rename from src/org/labkey/test/tests/SpecimenExportTest.java rename to specimen/test/src/org/labkey/test/tests/SpecimenExportTest.java diff --git a/src/org/labkey/test/tests/SpecimenGridExportTest.java b/specimen/test/src/org/labkey/test/tests/SpecimenGridExportTest.java similarity index 100% rename from src/org/labkey/test/tests/SpecimenGridExportTest.java rename to specimen/test/src/org/labkey/test/tests/SpecimenGridExportTest.java diff --git a/src/org/labkey/test/tests/SpecimenImportTest.java b/specimen/test/src/org/labkey/test/tests/SpecimenImportTest.java similarity index 100% rename from src/org/labkey/test/tests/SpecimenImportTest.java rename to specimen/test/src/org/labkey/test/tests/SpecimenImportTest.java diff --git a/src/org/labkey/test/tests/SpecimenMultipleImportTest.java b/specimen/test/src/org/labkey/test/tests/SpecimenMultipleImportTest.java similarity index 100% rename from src/org/labkey/test/tests/SpecimenMultipleImportTest.java rename to specimen/test/src/org/labkey/test/tests/SpecimenMultipleImportTest.java diff --git a/src/org/labkey/test/tests/SpecimenTest.java b/specimen/test/src/org/labkey/test/tests/SpecimenTest.java similarity index 100% rename from src/org/labkey/test/tests/SpecimenTest.java rename to specimen/test/src/org/labkey/test/tests/SpecimenTest.java From 37062c0c6d197319c8728f4863fd964e0bfb0be0 Mon Sep 17 00:00:00 2001 From: labkey-tchad Date: Fri, 25 Sep 2026 14:15:45 -0700 Subject: [PATCH 493/494] Move specimen tests to specimen module --- .../test/src/org/labkey/test/tests/study/SpecimenMergeTest.java | 0 .../test/src/org/labkey/test/tests/study/SpecimenReplaceTest.java | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename {study => specimen}/test/src/org/labkey/test/tests/study/SpecimenMergeTest.java (100%) rename {study => specimen}/test/src/org/labkey/test/tests/study/SpecimenReplaceTest.java (100%) diff --git a/study/test/src/org/labkey/test/tests/study/SpecimenMergeTest.java b/specimen/test/src/org/labkey/test/tests/study/SpecimenMergeTest.java similarity index 100% rename from study/test/src/org/labkey/test/tests/study/SpecimenMergeTest.java rename to specimen/test/src/org/labkey/test/tests/study/SpecimenMergeTest.java diff --git a/study/test/src/org/labkey/test/tests/study/SpecimenReplaceTest.java b/specimen/test/src/org/labkey/test/tests/study/SpecimenReplaceTest.java similarity index 100% rename from study/test/src/org/labkey/test/tests/study/SpecimenReplaceTest.java rename to specimen/test/src/org/labkey/test/tests/study/SpecimenReplaceTest.java From 8983de74ee39b3a2faa3f05117261121f4550fa8 Mon Sep 17 00:00:00 2001 From: labkey-tchad Date: Tue, 29 Sep 2026 09:59:03 -0700 Subject: [PATCH 494/494] Fix line endings --- .../test/tests/study/SpecimenReplaceTest.java | 182 +++++++++--------- 1 file changed, 91 insertions(+), 91 deletions(-) diff --git a/specimen/test/src/org/labkey/test/tests/study/SpecimenReplaceTest.java b/specimen/test/src/org/labkey/test/tests/study/SpecimenReplaceTest.java index 0f431007..5f28c2ce 100644 --- a/specimen/test/src/org/labkey/test/tests/study/SpecimenReplaceTest.java +++ b/specimen/test/src/org/labkey/test/tests/study/SpecimenReplaceTest.java @@ -1,91 +1,91 @@ -/* - * Copyright (c) 2016-2026 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.test.tests.study; - -import org.junit.Test; -import org.junit.experimental.categories.Category; -import org.labkey.test.BaseWebDriverTest; -import org.labkey.test.Locator; -import org.labkey.test.categories.Daily; -import org.labkey.test.categories.Specimen; -import org.labkey.test.util.StudyHelper; - -import java.io.File; - -@Category({Daily.class, Specimen.class}) -@BaseWebDriverTest.ClassTimeout(minutes = 6) -public class SpecimenReplaceTest extends SpecimenMergeTest -{ - protected static final File LAB_EDITED_SPECIMENS = StudyHelper.getSpecimenArchiveFile("lab19edit.specimens"); - protected static final File LAB15_SPECIMENS = StudyHelper.getSpecimenArchiveFile("lab15.specimens"); - protected static final File LAB20_SPECIMENS = StudyHelper.getSpecimenArchiveFile("lab20.specimens"); - protected static final File LAB21_SPECIMENS = StudyHelper.getSpecimenArchiveFile("lab21.specimens"); - - @Override - @Test - public void testSteps() - { - setUpSteps(); - importFirstFileSet(); - - verifyReplaceWithIdenticalFiles(); - verifyReplaceWithSlightlyModifiedData(); - verifyReplaceWithNewData(); - } - - private void verifyReplaceWithNewData() - { - SpecimenImporter importer = new SpecimenImporter(new File(_studyDataRoot), new File[] {LAB15_SPECIMENS}, SPECIMEN_TEMP_DIR, FOLDER_NAME, ++pipelineJobCount); - importer.setExpectError(true); - importer.importAndWaitForComplete(); - //go to individual vial list - goToIndividualvialsDRT(); - assertElementPresent(Locator.paginationText(12)); - - //entry for participant 999320812 have been replaced with 123123123 - assertTextPresent("999320812"); - assertTextNotPresent("123123123"); - } - - private void verifyReplaceWithSlightlyModifiedData() - { - SpecimenImporter importer = new SpecimenImporter(new File(_studyDataRoot), new File[] {LAB_EDITED_SPECIMENS}, SPECIMEN_TEMP_DIR, FOLDER_NAME, ++pipelineJobCount); - importer.setExpectError(true); - importer.importAndWaitForComplete(); - //go to individual vial list - goToIndividualvialsDRT(); - assertElementPresent(Locator.paginationText(1, 100, 666)); - - //entry for participant 999320812 have been replaced with 123123123 - assertTextNotPresent("999320812"); - assertTextPresent("123123123"); - } - - private void verifyReplaceWithIdenticalFiles() - { - pipelineJobCount += 3; - importFirstFileSet(); - goToIndividualvialsDRT(); - - assertElementPresent(Locator.paginationText(1, 100, 667)); - } - - private void goToIndividualvialsDRT() - { - clickTab("Specimen Data"); - waitAndClickAndWait(Locator.linkWithText("By Individual Vial")); - } -} +/* + * Copyright (c) 2016-2026 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.test.tests.study; + +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.labkey.test.BaseWebDriverTest; +import org.labkey.test.Locator; +import org.labkey.test.categories.Daily; +import org.labkey.test.categories.Specimen; +import org.labkey.test.util.StudyHelper; + +import java.io.File; + +@Category({Daily.class, Specimen.class}) +@BaseWebDriverTest.ClassTimeout(minutes = 6) +public class SpecimenReplaceTest extends SpecimenMergeTest +{ + protected static final File LAB_EDITED_SPECIMENS = StudyHelper.getSpecimenArchiveFile("lab19edit.specimens"); + protected static final File LAB15_SPECIMENS = StudyHelper.getSpecimenArchiveFile("lab15.specimens"); + protected static final File LAB20_SPECIMENS = StudyHelper.getSpecimenArchiveFile("lab20.specimens"); + protected static final File LAB21_SPECIMENS = StudyHelper.getSpecimenArchiveFile("lab21.specimens"); + + @Override + @Test + public void testSteps() + { + setUpSteps(); + importFirstFileSet(); + + verifyReplaceWithIdenticalFiles(); + verifyReplaceWithSlightlyModifiedData(); + verifyReplaceWithNewData(); + } + + private void verifyReplaceWithNewData() + { + SpecimenImporter importer = new SpecimenImporter(new File(_studyDataRoot), new File[] {LAB15_SPECIMENS}, SPECIMEN_TEMP_DIR, FOLDER_NAME, ++pipelineJobCount); + importer.setExpectError(true); + importer.importAndWaitForComplete(); + //go to individual vial list + goToIndividualvialsDRT(); + assertElementPresent(Locator.paginationText(12)); + + //entry for participant 999320812 have been replaced with 123123123 + assertTextPresent("999320812"); + assertTextNotPresent("123123123"); + } + + private void verifyReplaceWithSlightlyModifiedData() + { + SpecimenImporter importer = new SpecimenImporter(new File(_studyDataRoot), new File[] {LAB_EDITED_SPECIMENS}, SPECIMEN_TEMP_DIR, FOLDER_NAME, ++pipelineJobCount); + importer.setExpectError(true); + importer.importAndWaitForComplete(); + //go to individual vial list + goToIndividualvialsDRT(); + assertElementPresent(Locator.paginationText(1, 100, 666)); + + //entry for participant 999320812 have been replaced with 123123123 + assertTextNotPresent("999320812"); + assertTextPresent("123123123"); + } + + private void verifyReplaceWithIdenticalFiles() + { + pipelineJobCount += 3; + importFirstFileSet(); + goToIndividualvialsDRT(); + + assertElementPresent(Locator.paginationText(1, 100, 667)); + } + + private void goToIndividualvialsDRT() + { + clickTab("Specimen Data"); + waitAndClickAndWait(Locator.linkWithText("By Individual Vial")); + } +}