diff --git a/onprc_ehr/resources/queries/study/processingSerology.sql b/onprc_ehr/resources/queries/study/processingSerology.sql index 94330ec5d..079f45516 100644 --- a/onprc_ehr/resources/queries/study/processingSerology.sql +++ b/onprc_ehr/resources/queries/study/processingSerology.sql @@ -26,6 +26,12 @@ SELECT t.isPCRRequired, t.isPCRCurrent, + t.lastBchem, + t.daysSinceBchem, + t.isBchemRequired, + t.isBchemCurrent, + + CASE WHEN (t.isSRVRequired = true AND t.isSRVCurrent = false) THEN 4 ELSE 0 @@ -36,6 +42,38 @@ SELECT ELSE 0 END as PCRbloodVol, +-- CASE +-- WHEN (t.isESPFRequired = true AND t.isESPFCurrent = false ) THEN 4 +-- ELSE 0 +-- END as ESPFbloodVol, + + -----CBC + CASE + + WHEN (t.isCBCRequired2 = true AND t.isCBCCurrent2 = false ) THEN 1 + WHEN (t.isCBCRequired5 = true AND t.isCBCCurrent5 = false ) THEN 1 + WHEN (t.isCBCRequired3 = true AND t.isCBCCurrent3 = false ) THEN 1 + WHEN (t.isCBCRequired4 = true AND t.isCBCCurrent4 = false ) THEN 1 + WHEN (t.isCBCRequired1 = true AND t.isCBCCurrent1 = false ) THEN 1 + ELSE 0 + END as CBCbloodVol, + + + -----Comprehensive Chemistry + CASE + WHEN (t.isCChemRequired4 = true AND t.isCChemCurrent4 = false ) THEN 2 + WHEN (t.isCChemRequired2 = true AND t.isCChemCurrent2 = false ) THEN 2 + WHEN (t.isCChemRequired3 = true AND t.isCChemCurrent3 = false ) THEN 2 + WHEN (t.isCChemRequired1 = true AND t.isCChemCurrent1 = false ) THEN 2 + + ELSE 0 + END as CChembloodVol, + + -----Basic Chemistry + CASE + WHEN (t.isBChemRequired = true AND t.isBChemCurrent = false ) THEN 2 + ELSE 0 + END as BchembloodVol FROM ( @@ -64,7 +102,159 @@ SELECT CASE WHEN (d.Id.age.ageInDays > 180 ) THEN true ELSE false - END as isPCRRequired + END as isPCRRequired, + +-- espf.lastDate as lastESPF, +-- timestampdiff('SQL_TSI_DAY', espf.lastDate, now()) as daysSinceESPF, +-- CASE +-- WHEN ( (timestampdiff('SQL_TSI_DAY', espf.lastDate, now()) > 180) OR espf.lastDate is Null ) THEN false +-- ELSE true +-- END as isESPFCurrent, + +-- ESPF---> WHEN (year(now()) = year(pcr.lastDate) And (month(pcr.lastDate) = "January" OR month(pcr.lastDate) = "July")) +-- THEN true + + +-- CASE +-- WHEN (d.Id.age.ageInDays > 0 ) THEN true +-- ELSE false +-- END as isESPFRequired, + + + ------ All CBC Sections + + cbc.lastDate as lastCBC1, + timestampdiff('SQL_TSI_DAY', cbc.lastDate, now()) as daysSinceCBC1, + CASE + WHEN ( (timestampdiff('SQL_TSI_DAY', cbc.lastDate, now()) > 165 )AND d.Id.curLocation.area in ('Corral', 'Shelters', 'PENS' ) ) THEN false + ELSE true + END as isCBCCurrent1, + + CASE + WHEN (d.id.age.AgeInYears > 20 ) THEN true + ELSE false + END as isCBCRequired1, + + cbc.lastDate as lastCBC2, + timestampdiff('SQL_TSI_DAY', cbc.lastDate, now()) as daysSinceCBC2, + CASE + WHEN ( ( (timestampdiff('SQL_TSI_DAY', cbc.lastDate, now()) > 340) OR cbc.lastDate is null ) AND d.Id.curLocation.area in ('Corral', 'Shelters', 'PENS' ) AND (flg.Id is not null) ) THEN false + ELSE true + END as isCBCCurrent2, + + CASE + WHEN (d.Id.age.ageInDays > 0 ) THEN true + ELSE false + END as isCBCRequired2, + + cbc.lastDate as lastCBC3, + timestampdiff('SQL_TSI_DAY', cbc.lastDate, now()) as daysSinceCBC3, + CASE + WHEN ( (timestampdiff('SQL_TSI_DAY', cbc.lastDate, now()) > 340) AND d.Id.curLocation.area not in ('Corral', 'Shelters', 'PENS' ) ) THEN false + ELSE true + END as isCBCCurrent3, + + CASE + WHEN (d.id.age.AgeInYears > 12 ) THEN true + ELSE false + END as isCBCRequired3, + + + cbc.lastDate as lastCBC4, + timestampdiff('SQL_TSI_DAY', cbc.lastDate, now()) as daysSinceCBC4, + CASE + WHEN ( cbc.lastDate is null AND d.Id.curLocation.area not in ('Corral', 'Shelters', 'PENS' ) ) THEN false + ELSE true + END as isCBCCurrent4, + + CASE + WHEN (d.id.age.AgeInYears > 6 ) THEN true + ELSE false + END as isCBCRequired4, + + + cbc.lastDate as lastCBC5, + timestampdiff('SQL_TSI_DAY', cbc.lastDate, now()) as daysSinceCBC5, + CASE + WHEN ( ( (timestampdiff('SQL_TSI_DAY', cbc.lastDate, now()) > 180) OR cbc.lastDate is null ) AND (nts.Id is not null) ) THEN false + ELSE true + END as isCBCCurrent5, + + CASE + WHEN (d.Id.age.ageInDays > 0 ) THEN true + ELSE false + END as isCBCRequired5, + + + + ----- All Comp Chemistry sections + + + cchem.lastDate as lastCChem1, + timestampdiff('SQL_TSI_DAY', cchem.lastDate, now()) as daysSinceCChem1, + CASE + WHEN ( ( (timestampdiff('SQL_TSI_DAY', cchem.lastDate, now()) > 165) OR cchem.lastDate is null ) AND d.Id.curLocation.area in ('Corral', 'Shelters', 'PENS' ) ) THEN false + ELSE true + END as isCChemCurrent1, + + CASE + WHEN (d.id.age.AgeInYears > 20 ) THEN true + ELSE false + END as isCChemRequired1, + + + cchem.lastDate as lastCChem2, + timestampdiff('SQL_TSI_DAY', cchem.lastDate, now()) as daysSinceCChem2, + CASE + WHEN ( ( (timestampdiff('SQL_TSI_DAY', cchem.lastDate, now()) > 340) OR cchem.lastDate is null ) AND d.Id.curLocation.area in ('Corral', 'Shelters', 'PENS' ) AND (flg.Id is not null) ) THEN false + ELSE true + END as isCChemCurrent2, + + CASE + WHEN (d.Id.age.ageInDays > 0 ) THEN true + ELSE false + END as isCChemRequired2, + + cchem.lastDate as lastCChem3, + timestampdiff('SQL_TSI_DAY', cchem.lastDate, now()) as daysSinceCChem3, + CASE + WHEN ( (timestampdiff('SQL_TSI_DAY', cchem.lastDate, now()) > 180) AND d.Id.curLocation.area not in ('Corral', 'Shelters', 'PENS' ) ) THEN false + ELSE true + END as isCChemCurrent3, + + CASE + WHEN (d.id.age.AgeInYears >= 18 ) THEN true + ELSE false + END as isCChemRequired3, + + cchem.lastDate as lastCChem4, + timestampdiff('SQL_TSI_DAY', cchem.lastDate, now()) as daysSinceCChem4, + CASE + WHEN ( ( (timestampdiff('SQL_TSI_DAY', cchem.lastDate, now()) > 180 ) OR cchem.lastdate is null ) AND (nts.Id is not null ) ) THEN false + ELSE true + END as isCChemCurrent4, + + CASE + WHEN (d.Id.age.ageInDays > 0 ) THEN true + ELSE false + END as isCChemRequired4, + + + ------------- Basic Chemistry + + + bchem.lastDate as lastBChem, + timestampdiff('SQL_TSI_DAY', bchem.lastDate, now()) as daysSinceBChem, + CASE + WHEN ( ( (timestampdiff('SQL_TSI_DAY', bchem.lastDate, now()) > 340 ) OR bchem.lastDate is null ) AND (nts.Id is null) AND d.Id.curLocation.area not in ('Corral', 'Shelters', 'PENS' ) ) THEN false + ELSE true + END as isBChemCurrent, + + CASE + WHEN (d.id.age.AgeInYears >= 6 AND d.id.age.AgeInYears < 18 ) THEN true + ELSE false + END as isBChemRequired + FROM study.demographics d @@ -73,11 +263,21 @@ LEFT JOIN ( s.id, max(s.date) as lastDate FROM study.blood s - WHERE (s.additionalservices like 'SPF Surveillance%' or s.additionalservices like 'Compromised SPF%') + WHERE (s.additionalservices like 'SPF Surveillance - Annual%' or s.additionalservices like 'Compromised SPF Surveillance%') GROUP BY s.id ) srv ON (srv.id = d.id) +LEFT JOIN ( + SELECT + k.id, + max(k.date) as lastDate + FROM study.blood k + WHERE (k.additionalservices like '%ESPF Surveillance - Semiannual%') + GROUP BY k.id + +) espf ON (espf.id = d.id) + LEFT JOIN ( SELECT b.id, @@ -88,6 +288,60 @@ LEFT JOIN ( ) pcr ON (pcr.id = d.id) +LEFT JOIN ( + SELECT + j.id, + max(j.date) as lastDate + FROM study.blood j + WHERE j.additionalservices like '%CBC with automated differential%' + GROUP BY j.id + +) cbc ON (cbc.id = d.id) + +LEFT JOIN ( + SELECT + m.id, + max(m.date) as lastDate + FROM study.blood m + WHERE m.additionalservices like '%Comprehensive Chemistry panel in-house%' + GROUP BY m.id + +) cchem ON (cchem.id = d.id) + + +LEFT JOIN ( + SELECT + t.id, + max(t.date) as lastDate + FROM study.blood t + WHERE t.additionalservices like '%Basic Chemistry Panel%' + GROUP BY t.id + +) bchem ON (bchem.id = d.id) + +LEFT JOIN ( + SELECT + n.id, + max(n.date) as lastDate + FROM study.flags n + WHERE n.flag.category ='Behavior Flag' And n.flag.value = 'Socially important' + And n.enddate is null + GROUP BY n.id + +) flg ON (flg.id = d.id) + + +LEFT JOIN ( + SELECT + p.id, + max(p.date) as lastDate + FROM study.flags p + WHERE p.flag.category ='Assign Alias' And p.flag.value = 'Assignment pool' + And p.enddate is null + GROUP BY p.id + +) nts ON (nts.id = d.id) + WHERE d.calculated_status = 'Alive' diff --git a/onprc_ehr/src/org/labkey/onprc_ehr/ONPRC_EHRController.java b/onprc_ehr/src/org/labkey/onprc_ehr/ONPRC_EHRController.java index 40f2943c9..6beb78199 100644 --- a/onprc_ehr/src/org/labkey/onprc_ehr/ONPRC_EHRController.java +++ b/onprc_ehr/src/org/labkey/onprc_ehr/ONPRC_EHRController.java @@ -48,6 +48,7 @@ import org.labkey.api.view.ActionURL; import org.springframework.validation.BindException; import org.springframework.validation.Errors; +import org.labkey.api.security.SessionApiKeyManager; import java.sql.ResultSet; import java.sql.SQLException; @@ -174,7 +175,8 @@ public static class GetSessionIdAction extends MutatingApiAction @Override public Object execute(Object o, BindException errors) { - return Map.of("SessionId", getViewContext().getRequest().getSession(true).getId()); +// return Map.of("SessionId", getViewContext().getRequest().getSession(true).getId()); + return Map.of("SessionId", SessionApiKeyManager.get().getApiKey(getViewContext().getRequest(), "onprc_ehr.ssrs")); } } diff --git a/onprc_ehr/src/org/labkey/onprc_ehr/notification/ColonyAlertsNotification.java b/onprc_ehr/src/org/labkey/onprc_ehr/notification/ColonyAlertsNotification.java index 4d9079269..7d7c44d63 100644 --- a/onprc_ehr/src/org/labkey/onprc_ehr/notification/ColonyAlertsNotification.java +++ b/onprc_ehr/src/org/labkey/onprc_ehr/notification/ColonyAlertsNotification.java @@ -942,7 +942,7 @@ protected void incompleteBirthRecords(final Container c, User u, final StringBui Results rs = new ResultsImpl(object, cols); String url = getExecuteQueryUrl(c, "study", "demographics", null); url = url.replaceAll("executeQuery.view", "updateQuery.view"); - url = url.replaceAll("/query/", "/ehr/"); + url = url.replaceAll("/query-", "/ehr-"); msg.append(""); msg.append("" + rs.getString("Id") + ""); diff --git a/onprc_ehr/test/src/org/labkey/test/tests/onprc_ehr/ONPRC_SsrsSessionKeyTest.java b/onprc_ehr/test/src/org/labkey/test/tests/onprc_ehr/ONPRC_SsrsSessionKeyTest.java new file mode 100644 index 000000000..09d268fba --- /dev/null +++ b/onprc_ehr/test/src/org/labkey/test/tests/onprc_ehr/ONPRC_SsrsSessionKeyTest.java @@ -0,0 +1,204 @@ +/* + * 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.onprc_ehr; + +import org.json.JSONObject; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.labkey.test.BaseWebDriverTest; +import org.labkey.test.ModulePropertyValue; +import org.labkey.test.TestTimeoutException; +import org.labkey.test.WebTestHelper; +import org.labkey.test.categories.ONPRC; +import org.labkey.test.util.PasswordUtil; +import org.labkey.test.util.SimpleHttpRequest; +import org.labkey.test.util.SimpleHttpResponse; +import org.labkey.test.util.SqlserverOnlyTest; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.InputSource; + +import javax.xml.XMLConstants; +import javax.xml.parsers.DocumentBuilderFactory; +import java.io.IOException; +import java.io.StringReader; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +/** + * Exercises the SSRS session-key authentication path end to end, without a real SSRS server. + *

+ * Real-world flow being approximated: + * 1. A user opens the ONPRC printable reports page. ONPRC.Utils.preloadSession() POSTs to + * onprc_ehr-getSessionId.api, which returns a session key (see ONPRC_EHRController.GetSessionIdAction). + * 2. Clicking a report builds a URL to the SSRS server carrying that key as the "SessionId" parameter. + * 3. SSRS, running on a separate host with no LabKey cookie, calls back to a selectRows URL, passing the + * key as the "LabKeyTransformSessionId" query parameter. SecurityManager.getApiKey() reads it from the + * query string and SessionApiKeyManager resolves it back to the user's session. + *

+ * The only thing we fake is SSRS: the SSRSServerURL module property points back at this LabKey instance (the + * same trick AbstractGenericONPRC_EHRTest uses), and the cookieless callback is replayed with SimpleHttpRequest + * carrying ONLY the token on the URL (no session cookie, no Basic auth) -- exactly the SSRS condition. + */ +@Category({ONPRC.class}) +public class ONPRC_SsrsSessionKeyTest extends BaseWebDriverTest implements SqlserverOnlyTest +{ + @Override + protected String getProjectName() + { + return "ONPRC_SsrsSessionKeyTest Project"; + } + + @Override + public List getAssociatedModules() + { + return Arrays.asList("ehr", "onprc_ehr"); + } + + @BeforeClass + public static void setupProject() + { + ONPRC_SsrsSessionKeyTest init = getCurrentTest(); + init.doSetup(); + } + + private void doSetup() + { + _containerHelper.createProject(getProjectName(), null); + _containerHelper.enableModules(Arrays.asList("EHR", "ONPRC_EHR")); + + // Treat this LabKey instance as the fake SSRS target (mirrors AbstractGenericONPRC_EHRTest). preloadSession() + // does not actually need these, but setting them keeps the printable reports page behaving as in production. + setModuleProperties(Arrays.asList( + new ModulePropertyValue("ONPRC_EHR", "/" + getProjectName(), "SSRSServerURL", WebTestHelper.getBaseURL()), + new ModulePropertyValue("ONPRC_EHR", "/" + getProjectName(), "SSRSReportFolder", "DummySSRSFolder") + )); + } + @Override + protected void checkQueries() + { + // No-op, as this minimal test isn't mocking up the full ONPRC EHR folder context needed to make query validation pass + } + + @Test + public void testSsrsSessionKeyAuthentication() throws IOException + { + // 1) Real workflow: open the printable reports page, which fires ONPRC.Utils.preloadSession() + beginAt(WebTestHelper.buildURL("onprc_ehr", getProjectName(), "printableReports")); + + // 2) Harvest the token exactly as the SSRS link would receive it + String sessionKey = waitFor( + () -> (String) executeScript("return (window.ONPRC && ONPRC.Utils) ? ONPRC.Utils.sessionId : null;"), + "ONPRC.Utils.preloadSession() never populated a session key", WAIT_FOR_JAVASCRIPT); + assertNotNull("Session key was not preloaded", sessionKey); + + // The key must be a session key, NOT the raw JSESSIONID -- that is the whole point of the change. + String jsessionId = getDriver().manage().getCookieNamed("JSESSIONID").getValue(); + assertNotEquals("getSessionId returned the raw JSESSIONID instead of a session key", jsessionId, sessionKey); + + String expectedEmail = PasswordUtil.getUsername(); + + // 3) Simulate the SSRS callback: cookieless, no Basic auth, ONLY the token on the URL. + // 3a) Identity check via whoami -- proves the callback authenticates as the right user. + JSONObject whoAmI = cookielessGetJson(WebTestHelper.buildURL("login", getProjectName(), "whoami", + Map.of("LabKeyTransformSessionId", sessionKey))); + assertEquals("Token-authenticated callback resolved to the wrong user", expectedEmail, whoAmI.getString("email")); + + // 3b) Closest-to-real: the actual selectRows callback shape SSRS uses to fetch data. SSRS's XML data + // source extension requests the XML response format, so do the same and validate that the payload is + // well-formed XML containing the expected data row (the current user, filtered by email). + SimpleHttpResponse selectRows = cookielessGet(WebTestHelper.buildURL("query", getProjectName(), "selectRows", + Map.of("schemaName", "core", "query.queryName", "Users", "query.columns", "Email", + "query.Email~eq", expectedEmail, "respFormat", "xml", "LabKeyTransformSessionId", sessionKey))); + assertEquals("selectRows callback with a valid token should succeed", 200, selectRows.getResponseCode()); + + Document doc = parseXml(selectRows.getResponseBody()); + Element root = doc.getDocumentElement(); + assertEquals("Unexpected root element in selectRows XML response", "response", root.getTagName()); + Element rowsElement = (Element) root.getElementsByTagName("rows").item(0); + assertNotNull("selectRows XML response is missing the element", rowsElement); + NodeList rows = rowsElement.getElementsByTagName("element"); + assertTrue("selectRows XML response should contain at least one data row", rows.getLength() >= 1); + Node email = ((Element) rows.item(0)).getElementsByTagName("Email").item(0); + assertNotNull("Data row in selectRows XML response is missing the Email column", email); + assertEquals("Data row in selectRows XML response should be for the current user", expectedEmail, email.getTextContent()); + + // 4) Negative controls -- prove it is the token doing the work. + // 4a) No token -> guest (empty email) + JSONObject noToken = cookielessGetJson(WebTestHelper.buildURL("login", getProjectName(), "whoami")); + assertEquals("A cookieless callback with no token should be guest", "guest", noToken.getString("email")); + + // 4b) Bogus token -> guest + JSONObject bogus = cookielessGetJson(WebTestHelper.buildURL("login", getProjectName(), "whoami", + Map.of("LabKeyTransformSessionId", "not-a-real-session-key"))); + assertFalse("A cookieless callback with a bogus token should be guest", bogus.getBoolean("success")); + + // 5) Lifecycle: after the user logs out, the session key must stop working (auto-invalidated with the session). + signOut(); + JSONObject afterLogout = cookielessGetJson(WebTestHelper.buildURL("login", getProjectName(), "whoami", + Map.of("LabKeyTransformSessionId", sessionKey))); + assertFalse("A cookieless callback with a bogus token should be guest", afterLogout.getBoolean("success")); + } + + /** + * A request that carries ONLY the URL -- no session cookie and no Basic auth -- as SSRS would issue it. + */ + private SimpleHttpResponse cookielessGet(String url) throws IOException + { + SimpleHttpRequest request = new SimpleHttpRequest(url); + request.clearLogin(); // SimpleHttpRequest defaults to admin Basic auth; clear it to simulate SSRS + // Deliberately do NOT copySession(getDriver()), so no JSESSIONID cookie is sent. + return request.getResponse(); + } + + private JSONObject cookielessGetJson(String url) throws IOException + { + return new JSONObject(cookielessGet(url).getResponseBody()); + } + + /** + * Parse a response body, failing the test if it is not well-formed XML. + */ + private Document parseXml(String responseBody) + { + try + { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); + return factory.newDocumentBuilder().parse(new InputSource(new StringReader(responseBody))); + } + catch (Exception e) + { + throw new AssertionError("selectRows did not return well-formed XML. Body:\n" + responseBody, e); + } + } + + @Override + protected void doCleanup(boolean afterTest) throws TestTimeoutException + { + _containerHelper.deleteProject(getProjectName(), afterTest); + } +} \ No newline at end of file diff --git a/onprc_reports/resources/queries/study/processingBloodDraws.query.xml b/onprc_reports/resources/queries/study/processingBloodDraws.query.xml index 32b18373b..98486764e 100644 --- a/onprc_reports/resources/queries/study/processingBloodDraws.query.xml +++ b/onprc_reports/resources/queries/study/processingBloodDraws.query.xml @@ -13,6 +13,9 @@ Is U42? + + Is U24? + Parentage PTT (mL) @@ -22,12 +25,24 @@ PCR PTT (mL) + + + DNA Pink/PTT (mL) Serology RTT (mL) + + CBC PTT (mL) + + + COMP Chem RTT (mL) + + + BASIC Chem RTT (mL) + Genetics Blood Vol (mL) diff --git a/onprc_reports/resources/queries/study/processingBloodDraws.sql b/onprc_reports/resources/queries/study/processingBloodDraws.sql index dbe698f9d..ce3bf2e12 100644 --- a/onprc_reports/resources/queries/study/processingBloodDraws.sql +++ b/onprc_reports/resources/queries/study/processingBloodDraws.sql @@ -8,22 +8,47 @@ SELECT CASE WHEN (a.Id IS NULL) THEN 'N' ELSE 'Y' - END as isU42, + END as isU42, --- 0492-02 + CASE + WHEN (b.Id IS NULL) THEN 'N' + ELSE 'Y' + END as isU24, --- 0492-03 Case when (a.Id IS NOT NULL) And (s.PCRbloodVol > 0 ) then 2 ELSE 0 End as PCRBloodVolume, s.srvBloodVol as serologyBloodVol, + +-- Case when ( b.Id IS NOT NULL ) And (s.ESPFBloodVol > 0 ) then 4 +-- ELSE +-- 0 +-- End as ESPFBloodVol, + + + coalesce(s.CBCbloodVol,0) as totalCBCVol, + coalesce(s.CChembloodVol,0) as totalCompChemBloodVol, + coalesce(s.BChembloodVol,0) as BasicChemBloodVol, + g.parentageBloodDrawVol, g.mhcBloodDrawVol, g.dnaBloodDrawVol, g.totalBloodDrawVol as geneticsBloodVol, - coalesce(s.srvBloodVol, 0) + Case when (a.Id IS NOT NULL) And (s.pcrbloodVol > 0 ) then 2 + + + coalesce(s.srvBloodVol, 0) + + Case when (a.Id IS NOT NULL) And (s.pcrbloodVol > 0 ) then 2 ELSE 0 - End + coalesce(g.totalBloodDrawVol, 0) as totalBloodDrawVol, + End + +-- Case when (b.Id IS NOT NULL) And (s.ESPFBloodVol > 0 ) then 4 +-- ELSE +-- 0 +-- End + + coalesce(s.CBCbloodVol,0) + + coalesce(s.CChembloodVol,0) + coalesce(s.BChembloodVol,0) + + coalesce(g.totalBloodDrawVol, 0) as totalBloodDrawVol, (select k.room from study.housing k where k.Id =d.Id And k.enddate is null) as currentlocationroom, (select coalesce(k.cage, ' ') from study.housing k where k.Id =d.Id And k.enddate is null) as currentlocationcage, @@ -39,3 +64,13 @@ FROM study.assignment a WHERE a.isActive = true and a.project.name = javaConstant('org.labkey.onprc_ehr.ONPRC_EHRManager.U42_PROJECT') GROUP BY a.Id ) a ON (a.Id = d.Id) + +LEFT JOIN (SELECT + b.Id + + FROM study.assignment b + WHERE b.isActive = true and b.project.name = javaConstant('org.labkey.onprc_ehr.ONPRC_EHRManager.U24_PROJECT') + GROUP BY b.Id +) b ON (b.Id = d.Id) + + diff --git a/onprc_reports/resources/queries/study/processingBloodDraws/.qview.xml b/onprc_reports/resources/queries/study/processingBloodDraws/.qview.xml index 7921a2716..08f443570 100644 --- a/onprc_reports/resources/queries/study/processingBloodDraws/.qview.xml +++ b/onprc_reports/resources/queries/study/processingBloodDraws/.qview.xml @@ -5,9 +5,14 @@ + + + + +