diff --git a/EHR_ComplianceDB/api-src/org/labkey/ehr_compliancedb/api/EHR_ComplianceService.java b/EHR_ComplianceDB/api-src/org/labkey/ehr_compliancedb/api/EHR_ComplianceService.java new file mode 100644 index 000000000..163762e73 --- /dev/null +++ b/EHR_ComplianceDB/api-src/org/labkey/ehr_compliancedb/api/EHR_ComplianceService.java @@ -0,0 +1,22 @@ +package org.labkey.ehr_compliancedb.api; + +import org.labkey.api.data.Container; + +/** + * Created by jon on 3/22/16. + */ +public abstract class EHR_ComplianceService { + static private EHR_ComplianceService _service = null; + + static public EHR_ComplianceService get() { + return _service; + } + + public static void setInstance(EHR_ComplianceService instance) { + _service = instance; + } + + public abstract String getComplianceModuleName(); + + public abstract Container getEmployeeContainer(Container c); +} diff --git a/EHR_ComplianceDB/resources/module.xml b/EHR_ComplianceDB/resources/module.xml index 8d95f5b68..ec54c55ca 100644 --- a/EHR_ComplianceDB/resources/module.xml +++ b/EHR_ComplianceDB/resources/module.xml @@ -14,6 +14,13 @@ This is the default view to show on the employees table + + true + + ADMIN + + This is the containerPath to the folder holding the list of PDFs shared between public and private folder. The PDF list has a Linked schema between both folders, the actual files have to reside in the public folder Use of slashes is very important - it should be in the format '/myProject/compliance' + diff --git a/EHR_ComplianceDB/resources/queries/ehr_compliancedb/MostRecentCompletionDates.sql b/EHR_ComplianceDB/resources/queries/ehr_compliancedb/MostRecentCompletionDates.sql new file mode 100644 index 000000000..a7ed96d66 --- /dev/null +++ b/EHR_ComplianceDB/resources/queries/ehr_compliancedb/MostRecentCompletionDates.sql @@ -0,0 +1,14 @@ +PARAMETERS (RequirementName VARCHAR DEFAULT 'SOP REVIEW-ANNUAL') + +SELECT t1.employeeid, t3.lastname, t3.firstname, t1.requirementname, t1.date +FROM ehr_compliancedb.completiondates t1 + JOIN ehr_compliancedb.employees t3 + ON t1.employeeid = t3.employeeid +WHERE EXISTS (SELECT 1 + FROM ehr_compliancedb.completiondates t2 + WHERE t1.employeeid = t2.employeeid + AND t1.requirementname = t2.requirementname + AND UPPER(t2.requirementname) = UPPER(RequirementName) + GROUP BY t2.employeeid, t2.requirementname + HAVING t1.date = MAX(t2.date)) +ORDER BY LOWER(t1.employeeid) ASC diff --git a/EHR_ComplianceDB/resources/queries/ehr_compliancedb/SOPDateLastRead.query.xml b/EHR_ComplianceDB/resources/queries/ehr_compliancedb/SOPDateLastRead.query.xml deleted file mode 100644 index e253428a4..000000000 --- a/EHR_ComplianceDB/resources/queries/ehr_compliancedb/SOPDateLastRead.query.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - true - - lists - sops - id - - - - SOP_ID -
-
-
-
diff --git a/EHR_ComplianceDB/resources/queries/ehr_compliancedb/SOPDateLastRead.sql b/EHR_ComplianceDB/resources/queries/ehr_compliancedb/SOPDateLastRead.sql deleted file mode 100644 index 9961edf49..000000000 --- a/EHR_ComplianceDB/resources/queries/ehr_compliancedb/SOPDateLastRead.sql +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (c) 2012-2019 LabKey Corporation - * - * Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0 - */ -SELECT - -e.employeeId, -sop.id as SOP_ID, -sop.name, -sop.pdf, -sop.Spanish_PDF, -sop.video, -T1.LastRead, -sop.activeDate - - -FROM ehr_compliancedb.employees e - - -JOIN -( SELECT max(t.date) as LastRead, t.SOPID, t.EmployeeId from ehr_compliancedb.SOPdates t group by t.employeeid, t.sopid) T1 - ON (T1.employeeId = e.employeeId ) - -LEFT JOIN lists.SOPs sop - ON (T1.SOPID = sop.id) \ No newline at end of file diff --git a/EHR_ComplianceDB/resources/queries/ehr_compliancedb/SOPDateLastRead/.qview.xml b/EHR_ComplianceDB/resources/queries/ehr_compliancedb/SOPDateLastRead/.qview.xml index cc4f75c8a..493cbd4c2 100644 --- a/EHR_ComplianceDB/resources/queries/ehr_compliancedb/SOPDateLastRead/.qview.xml +++ b/EHR_ComplianceDB/resources/queries/ehr_compliancedb/SOPDateLastRead/.qview.xml @@ -6,7 +6,7 @@ - + diff --git a/EHR_ComplianceDB/resources/queries/ehr_compliancedb/SOPrequirements.query.xml b/EHR_ComplianceDB/resources/queries/ehr_compliancedb/SOPrequirements.query.xml deleted file mode 100644 index 643c6a89a..000000000 --- a/EHR_ComplianceDB/resources/queries/ehr_compliancedb/SOPrequirements.query.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - true - - lists - sops - id - - - - SOP_ID -
-
-
-
diff --git a/EHR_ComplianceDB/resources/queries/ehr_compliancedb/SOPrequirements.sql b/EHR_ComplianceDB/resources/queries/ehr_compliancedb/SOPrequirements.sql deleted file mode 100644 index 947aba466..000000000 --- a/EHR_ComplianceDB/resources/queries/ehr_compliancedb/SOPrequirements.sql +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (c) 2012-2019 LabKey Corporation - * - * Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0 - */ -SELECT - e.employeeid, - sop.id as SOP_ID, - sop.name, - sop.pdf, - sop.Spanish_PDF, - e.email, - T1.LastRead, - - --we calculate the time until renewal - --this subquery was written before I changed this to use joins - --it is no longer needed, but i kept it b/c is uses age_in_months and is working - CONVERT( - CASE - WHEN (T1.LastRead IS NULL) THEN - 0 - ELSE - COALESCE( - --(12 - age_in_months(T1.MostRecentDate, curdate())) , 0) - - (SELECT (12 - age_in_months(sq1.MostRecentDate, curdate())) AS TimeUntilRenewal - FROM - (SELECT max(t.date) AS MostRecentDate, t.SOPID, t.EmployeeId FROM ehr_compliancedb.SOPdates t GROUP BY t.EmployeeId, t.SOPID) sq1 - WHERE sq1.SOPID = sc.SOP_ID AND e.employeeid = sq1.EmployeeId), 0) - END, double) - AS MonthsUntilRenewal, - - CASE - WHEN sc.sop_id IS NULL then FALSE - ELSE TRUE - END as required - -FROM ehr_compliancedb.Employees e - -CROSS JOIN lists.SOPs sop - --ON (e.category = sc.category) - -LEFT JOIN ehr_compliancedb.SOPbyCategory sc - ON (e.category = sc.category AND sop.id = sc.sop_id) - -LEFT JOIN - (SELECT max(t.date) AS LastRead, t.SOPID, t.EmployeeId FROM ehr_compliancedb.SOPdates t GROUP BY t.EmployeeId, t.SOPID) T1 - ON (T1.SOPID = sc.SOP_ID AND T1.EmployeeId = e.employeeid) - -WHERE - ---active employees only -e.EndDateCoalesced >= curdate() \ No newline at end of file diff --git a/EHR_ComplianceDB/resources/queries/ehr_compliancedb/SOPrequirements/.qview.xml b/EHR_ComplianceDB/resources/queries/ehr_compliancedb/SOPrequirements/.qview.xml deleted file mode 100644 index 5a6c2cd59..000000000 --- a/EHR_ComplianceDB/resources/queries/ehr_compliancedb/SOPrequirements/.qview.xml +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/EHR_ComplianceDB/resources/queries/ehr_compliancedb/SOPrequirements/All SOPs.qview.xml b/EHR_ComplianceDB/resources/queries/ehr_compliancedb/SOPrequirements/All SOPs.qview.xml deleted file mode 100644 index 29ae35d0c..000000000 --- a/EHR_ComplianceDB/resources/queries/ehr_compliancedb/SOPrequirements/All SOPs.qview.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/EHR_ComplianceDB/resources/queries/ehr_compliancedb/SOPrequirements/To Read Standard.qview.xml b/EHR_ComplianceDB/resources/queries/ehr_compliancedb/SOPrequirements/To Read Standard.qview.xml deleted file mode 100644 index 2a29d1fbc..000000000 --- a/EHR_ComplianceDB/resources/queries/ehr_compliancedb/SOPrequirements/To Read Standard.qview.xml +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/EHR_ComplianceDB/resources/queries/ehr_compliancedb/SOPrequirements/To Read.qview.xml b/EHR_ComplianceDB/resources/queries/ehr_compliancedb/SOPrequirements/To Read.qview.xml deleted file mode 100644 index 71ba14e17..000000000 --- a/EHR_ComplianceDB/resources/queries/ehr_compliancedb/SOPrequirements/To Read.qview.xml +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/EHR_ComplianceDB/resources/queries/ehr_compliancedb/employees/Active Employees.qview.xml b/EHR_ComplianceDB/resources/queries/ehr_compliancedb/employees/Active Employees.qview.xml index a6c0a4b9a..5a75ffe12 100644 --- a/EHR_ComplianceDB/resources/queries/ehr_compliancedb/employees/Active Employees.qview.xml +++ b/EHR_ComplianceDB/resources/queries/ehr_compliancedb/employees/Active Employees.qview.xml @@ -1,4 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/EHR_ComplianceDB/resources/queries/ehr_compliancedb/recentlyActivatedSOPs.sql b/EHR_ComplianceDB/resources/queries/ehr_compliancedb/recentlyActivatedSOPs.sql deleted file mode 100644 index 418f79d1a..000000000 --- a/EHR_ComplianceDB/resources/queries/ehr_compliancedb/recentlyActivatedSOPs.sql +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2012 LabKey Corporation - * - * Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0 - */ -SELECT -e.employeeId, -sc.sop_id, -sop.name, -sop.pdf, -sop.Spanish_PDF, -sop.video, -T1.LastRead, - -sop.activeDate, -CONVERT( - CASE - WHEN (T1.LastRead is NULL) Then - 0 - ELSE - COALESCE( (SELECT (12-age_in_months(sq1.MostRecentDate, curdate())) as TimeUntilRenewal - FROM - (SELECT max(t.date) as MostRecentDate, t.sopid, t.employeeid from ehr_compliancedb.sopdates t group by t.employeeid, t.sopid) sq1 - where sq1.sopid = sc.sop_id and e.employeeid = sq1.employeeid), 0) - END, double) AS -MonthsUntilRenewal, - - CASE - WHEN sc.sop_id is null then false - else true - end as -required - -from ehr_compliancedb.employees e - -cross join lists.SOPs sop - -LEFT JOIN ehr_compliancedb.SOPbyCategory sc ON (e.category = sc.category and sop.id = sc.sop_id) - -LEFT JOIN (SELECT max(t.date) as lastRead, t.sopid, t.employeeid from ehr_compliancedb.sopdates t group by t.employeeid, t.sopid) t1 on (e.employeeid = t1.employeeid and sc.sop_id = t1.sopid) - -where e.employeeid is not null \ No newline at end of file diff --git a/EHR_ComplianceDB/resources/views/SOPadmin.html b/EHR_ComplianceDB/resources/views/SOPadmin.html deleted file mode 100644 index aee194544..000000000 --- a/EHR_ComplianceDB/resources/views/SOPadmin.html +++ /dev/null @@ -1,26 +0,0 @@ - \ No newline at end of file diff --git a/EHR_ComplianceDB/resources/views/SOPadmin.view.xml b/EHR_ComplianceDB/resources/views/SOPadmin.view.xml deleted file mode 100644 index bfe464d68..000000000 --- a/EHR_ComplianceDB/resources/views/SOPadmin.view.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/EHR_ComplianceDB/resources/views/SOPadmin.webpart.xml b/EHR_ComplianceDB/resources/views/SOPadmin.webpart.xml deleted file mode 100644 index a99864a2a..000000000 --- a/EHR_ComplianceDB/resources/views/SOPadmin.webpart.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/EHR_ComplianceDB/resources/views/begin.html b/EHR_ComplianceDB/resources/views/begin.html index 7e8967b80..b65c41e85 100644 --- a/EHR_ComplianceDB/resources/views/begin.html +++ b/EHR_ComplianceDB/resources/views/begin.html @@ -27,6 +27,8 @@ name: 'Employee List', url: '<%=contextPath%>/query' + container + '/executeQuery.view?schemaName=ehr_compliancedb&query.queryName=Employees' + (employeeDefaultView ? ('&query.viewName=' + employeeDefaultView) : '') },{ name: 'View/Edit Dates Employees Completed Requirements', url: '<%=contextPath%>/query' + container + '/executeQuery.view?schemaName=ehr_compliancedb&query.queryName=CompletionDates' + },{ + name: 'View Most Recent Dates Employees Completed Requirements', url: '<%=contextPath%>/query' + container + '/executeQuery.view?schemaName=ehr_compliancedb&query.queryName=MostRecentCompletionDates' },{ name: 'View LabKey Logins and Emails', url: '<%=contextPath%>/query' + container + '/executeQuery.view?schemaName=core&query.queryName=Users' }] diff --git a/EHR_ComplianceDB/src/org/labkey/ehr_compliancedb/EHR_ComplianceDBModule.java b/EHR_ComplianceDB/src/org/labkey/ehr_compliancedb/EHR_ComplianceDBModule.java index eac6434f0..766116000 100644 --- a/EHR_ComplianceDB/src/org/labkey/ehr_compliancedb/EHR_ComplianceDBModule.java +++ b/EHR_ComplianceDB/src/org/labkey/ehr_compliancedb/EHR_ComplianceDBModule.java @@ -24,6 +24,7 @@ import org.labkey.api.ldk.notification.NotificationService; import org.labkey.ehr_compliancedb.notification.EmployeeComplianceNotification; import org.labkey.api.module.ModuleContext; +import org.labkey.ehr_compliancedb.api.EHR_ComplianceService; import java.util.Collections; import java.util.Set; @@ -61,6 +62,8 @@ public boolean hasScripts() protected void init() { addController(CONTROLLER_NAME, EHR_ComplianceDBController.class); + + EHR_ComplianceService.setInstance(new EHR_ComplianceServiceImpl()); } @Override diff --git a/EHR_ComplianceDB/src/org/labkey/ehr_compliancedb/EHR_ComplianceServiceImpl.java b/EHR_ComplianceDB/src/org/labkey/ehr_compliancedb/EHR_ComplianceServiceImpl.java new file mode 100644 index 000000000..3cd204cb9 --- /dev/null +++ b/EHR_ComplianceDB/src/org/labkey/ehr_compliancedb/EHR_ComplianceServiceImpl.java @@ -0,0 +1,34 @@ +package org.labkey.ehr_compliancedb; + +import org.labkey.api.data.Container; +import org.labkey.api.data.ContainerManager; +import org.labkey.api.data.PropertyManager; +import org.labkey.api.module.ModuleLoader; +import org.labkey.api.module.ModuleProperty; +import org.labkey.ehr_compliancedb.api.EHR_ComplianceService; + +/** + * Created by jon on 3/22/16. + */ +public class EHR_ComplianceServiceImpl extends EHR_ComplianceService { + static public String EmployeeContainerPropertyName = "EmployeeContainer"; + + @Override + public String getComplianceModuleName() { + return EHR_ComplianceDBModule.SCHEMA_NAME; + } + + @Override + public Container getEmployeeContainer(Container c) { + EHR_ComplianceDBModule ehrCompliance = getModule(); + ModuleProperty prop = ehrCompliance.getModuleProperties().get(EmployeeContainerPropertyName); + + String containerPath = PropertyManager.getCoalescedProperty(PropertyManager.SHARED_USER, c, prop.getCategory(), EmployeeContainerPropertyName); + + return ContainerManager.getForPath(containerPath); + } + + public EHR_ComplianceDBModule getModule() { + return ModuleLoader.getInstance().getModule(EHR_ComplianceDBModule.class); + } +} diff --git a/Viral_Load_Assay/resources/module.xml b/Viral_Load_Assay/resources/module.xml index fa63f23f5..bb4eaf10e 100644 --- a/Viral_Load_Assay/resources/module.xml +++ b/Viral_Load_Assay/resources/module.xml @@ -1,5 +1,5 @@ - - - + + + diff --git a/Viral_Load_Assay/resources/schemas/dbscripts/postgresql/Viral_Load_Assay-12.25-12.26.sql b/Viral_Load_Assay/resources/schemas/dbscripts/postgresql/Viral_Load_Assay-12.25-12.26.sql new file mode 100644 index 000000000..f374dc3df --- /dev/null +++ b/Viral_Load_Assay/resources/schemas/dbscripts/postgresql/Viral_Load_Assay-12.25-12.26.sql @@ -0,0 +1,18 @@ +CREATE TABLE viral_load_assay.nucleic_acid +( + type CHARACTER VARYING(200) NOT NULL, + container entityid NOT NULL, + rowid SERIAL NOT NULL, + + CONSTRAINT pk_nucleic_acid PRIMARY KEY (rowid) +); + +CREATE TABLE viral_load_assay.source_material +( + type CHARACTER VARYING(200) NOT NULL, + liquid BOOLEAN NOT NULL, + container entityid NOT NULL, + rowid SERIAL NOT NULL, + + CONSTRAINT pk_source_material PRIMARY KEY (rowid) +); \ No newline at end of file diff --git a/Viral_Load_Assay/resources/schemas/dbscripts/postgresql/Viral_Load_Assay-12.26-12.27.sql b/Viral_Load_Assay/resources/schemas/dbscripts/postgresql/Viral_Load_Assay-12.26-12.27.sql new file mode 100644 index 000000000..54b19fe71 --- /dev/null +++ b/Viral_Load_Assay/resources/schemas/dbscripts/postgresql/Viral_Load_Assay-12.26-12.27.sql @@ -0,0 +1,6 @@ +INSERT INTO viral_load_assay.vl_instrument (instrument) + SELECT 'LC96' + WHERE + NOT EXISTS ( + SELECT instrument FROM viral_load_assay.vl_instrument WHERE instrument = 'LC96' + ); \ No newline at end of file diff --git a/Viral_Load_Assay/resources/schemas/dbscripts/postgresql/Viral_Load_Assay-12.27-12.28.sql b/Viral_Load_Assay/resources/schemas/dbscripts/postgresql/Viral_Load_Assay-12.27-12.28.sql new file mode 100644 index 000000000..17951e367 --- /dev/null +++ b/Viral_Load_Assay/resources/schemas/dbscripts/postgresql/Viral_Load_Assay-12.27-12.28.sql @@ -0,0 +1,9 @@ +BEGIN; + +ALTER TABLE viral_load_assay.nucleic_acid DROP CONSTRAINT pk_nucleic_acid; +ALTER TABLE viral_load_assay.nucleic_acid ADD CONSTRAINT pk_nucleic_acid PRIMARY KEY (type); + +ALTER TABLE viral_load_assay.source_material DROP CONSTRAINT pk_source_material; +ALTER TABLE viral_load_assay.source_material ADD CONSTRAINT pk_source_material PRIMARY KEY (type); + +COMMIT; \ No newline at end of file diff --git a/Viral_Load_Assay/resources/schemas/dbscripts/sqlserver/Viral_Load_Assay-12.25-12.26.sql b/Viral_Load_Assay/resources/schemas/dbscripts/sqlserver/Viral_Load_Assay-12.25-12.26.sql new file mode 100644 index 000000000..98f877233 --- /dev/null +++ b/Viral_Load_Assay/resources/schemas/dbscripts/sqlserver/Viral_Load_Assay-12.25-12.26.sql @@ -0,0 +1,18 @@ +CREATE TABLE viral_load_assay.nucleic_acid +( + type NVARCHAR(200) NOT NULL, + container entityid NOT NULL, + rowid INT IDENTITY(1, 1), + + CONSTRAINT pk_nucleic_acid PRIMARY KEY (rowid) +); + +CREATE TABLE viral_load_assay.source_material +( + type NVARCHAR(200) NOT NULL, + liquid BIT NOT NULL, + container entityid NOT NULL, + rowid INT IDENTITY(1, 1), + + CONSTRAINT pk_source_material PRIMARY KEY (rowid) +); \ No newline at end of file diff --git a/Viral_Load_Assay/resources/schemas/dbscripts/sqlserver/Viral_Load_Assay-12.26-12.27.sql b/Viral_Load_Assay/resources/schemas/dbscripts/sqlserver/Viral_Load_Assay-12.26-12.27.sql new file mode 100644 index 000000000..54b19fe71 --- /dev/null +++ b/Viral_Load_Assay/resources/schemas/dbscripts/sqlserver/Viral_Load_Assay-12.26-12.27.sql @@ -0,0 +1,6 @@ +INSERT INTO viral_load_assay.vl_instrument (instrument) + SELECT 'LC96' + WHERE + NOT EXISTS ( + SELECT instrument FROM viral_load_assay.vl_instrument WHERE instrument = 'LC96' + ); \ No newline at end of file diff --git a/Viral_Load_Assay/resources/schemas/dbscripts/sqlserver/Viral_Load_Assay-12.27-12.28.sql b/Viral_Load_Assay/resources/schemas/dbscripts/sqlserver/Viral_Load_Assay-12.27-12.28.sql new file mode 100644 index 000000000..52af38b90 --- /dev/null +++ b/Viral_Load_Assay/resources/schemas/dbscripts/sqlserver/Viral_Load_Assay-12.27-12.28.sql @@ -0,0 +1,5 @@ +ALTER TABLE viral_load_assay.nucleic_acid DROP CONSTRAINT pk_nucleic_acid; +ALTER TABLE viral_load_assay.nucleic_acid ADD CONSTRAINT pk_nucleic_acid PRIMARY KEY (type); + +ALTER TABLE viral_load_assay.source_material DROP CONSTRAINT pk_source_material; +ALTER TABLE viral_load_assay.source_material ADD CONSTRAINT pk_source_material PRIMARY KEY (type); diff --git a/Viral_Load_Assay/resources/schemas/viral_load_assay.xml b/Viral_Load_Assay/resources/schemas/viral_load_assay.xml index 0c4e4b682..72f48953e 100644 --- a/Viral_Load_Assay/resources/schemas/viral_load_assay.xml +++ b/Viral_Load_Assay/resources/schemas/viral_load_assay.xml @@ -150,4 +150,32 @@
+ + + DETAILED + Nucleic Acid + + + + + + + + +
+ + + DETAILED + Source Material + + + + + + + + + + +
diff --git a/Viral_Load_Assay/resources/web/Viral_Load_Assay/SampleData/LC_data.txt b/Viral_Load_Assay/resources/web/Viral_Load_Assay/SampleData/LC_data.txt deleted file mode 100644 index ab35e22de..000000000 --- a/Viral_Load_Assay/resources/web/Viral_Load_Assay/SampleData/LC_data.txt +++ /dev/null @@ -1,28 +0,0 @@ -TRUE 1 STD_15000000 15.19 1.47E+07 15000000 -TRUE 2 STD_1500000 18.65 1.47E+06 1500000 -TRUE 3 STD_150000 22.05 1.53E+05 150000 -TRUE 4 STD_15000 25.47 1.57E+04 15000 -TRUE 5 STD_1500 28.94 1.56E+03 1500 -TRUE 6 STD_150 32.56 1.41E+02 150 -TRUE 7 STD_15 34.16 1.50E+01 15 -TRUE 8 STD_3 3 -TRUE 9 STD_1.5 1.5 -TRUE 10 STD_0 0 -TRUE 11 d04061_2008.09.08_1.2_NW/JR 0 -TRUE 12 d45069_2008.09.08_1_NW/JR 0 -TRUE 13 d05099_2008.09.08_1.1_NW/JR**FP 38.1 [2.75E-6] 0 -TRUE 14 d06012_2008.09.08_1.1_NW/JR 18.51 1.61E+06 0 -TRUE 15 d07020_2008.09.08_1_NW/JR 0 -TRUE 16 d02321_2008.09.08_1_NW/JR 20.86 3.37E+05 0 -TRUE 17 d02589_2008.09.08_1_NW/JR 0 -TRUE 18 d03103_2008.09.08_1.4_NW/JR 0 -TRUE 19 d05114_2008.09.08_1.1_NW/JR 37.92 [7.52E-6] 0 -TRUE 20 d29112_2008.09.08_1_NW/JR 0 -TRUE 21 d29063_2008.09.08_1_NW/JR 0 -TRUE 22 de0114_2008.09.08_1_JG 29.72 9.32E+02 0 -TRUE 23 de0115_2008.09.08_1_JG 0 -TRUE 24 de0150_2008.09.08_1_JG 32.14 1.86E+02 0 -TRUE 25 de0152_2008.09.08_1_JG 24.46 3.08E+04 0 -TRUE 26 de0166_2008.09.08_1_JG 23.72 5.03E+04 0 -TRUE 27 CTL_negative 0 -TRUE 28 CTL_r02007_0.5 27.44 4.25E+03 0 diff --git a/Viral_Load_Assay/resources/web/Viral_Load_Assay/SampleData/lc480.txt b/Viral_Load_Assay/resources/web/Viral_Load_Assay/SampleData/lc480.txt index b8561f264..716af99ea 100644 --- a/Viral_Load_Assay/resources/web/Viral_Load_Assay/SampleData/lc480.txt +++ b/Viral_Load_Assay/resources/web/Viral_Load_Assay/SampleData/lc480.txt @@ -1,39 +1,93 @@ Include Pos Name Cp Concentration Standard Status -True A1 STD_15000000 16.69 1.76E7 15000000 -True B8 STD_5 38.30 5.65E0 5 -True B9 STD_3 39.85 3.00E0 3 ? - Detector Call uncertain -True B10 STD_0 0 -True C1 sd0159_2010.04.19_1_OC 24.49 7.93E4 0 -True C2 sd0160_2010.04.19_1_OC 0 -True C3 sd0338_2010.04.19_0.46_OC 0 -True C4 sd0339_2010.04.19_1_OC 0 -True C5 sd0340_2010.04.19_1_OC 0 -True C6 sd0341_2010.04.19_1_OC 0 -True C7 sd0345_2010.04.19_1_OC 0 -True C8 sd0346_2010.04.19_1_OC 0 -True C9 deAJ11_2010.04.21_1_JBS 0 -True C10 d90480_2010.04.21_1_JBS 0 -True C11 d95149_2010.04.21_1_JBS 30.54 1.20E3 0 -True C12 d96061_2010.04.21_1_JBS 0 -True E1 d56053_2010.04.21_1_JBS 24.99 5.60E4 0 -True E2 d28016_2010.04.21_1_JBS 32.07 4.15E2 0 -True E3 d98037_2010.04.21_1_JBS 30.01 1.73E3 0 -True E4 d96006_2010.04.21_1_JBS 35.85 3.01E1 0 -True E5 d95019_2010.04.21_1_JBS 25.49 3.96E4 0 -True E6 d04032_2010.04.21_1_JBS 26.52 1.94E4 0 -True E7 d03019_2010.04.21_1_JBS 0 -True E8 CTL_negative 0 -True E9 CTL_negative 0 -True E10 CTL_d02507_0.5 31.77 5.11E2 0 -True E11 CTL_negative 0 -True E12 CTL_r02007_0.5 29.58 2.33E3 0 -True G1 d02056_2010.04.19_1_LV 27.45 1.02E4 0 -True G2 d03830_2010.04.19_1_LV 31.04 8.50E2 0 -True G3 d04291_2010.04.19_1_LV 26.14 2.52E4 0 -True G4 d03504_2010.04.19_1_MR/AW 26.70 1.71E4 0 -True G5 dh6U10_2010.04.20_1.1_JR 33.60 1.43E2 0 -True G6 d95067_2010.04.20_1.15_JR 38.72 4.49E0 0 -True G7 d02088_2010.04.20_1.15_JR 23.68 1.38E5 0 -True G8 d03037_2010.04.20_1.2_JR 36.66 1.72E1 0 -True G9 d04145_2010.04.20_1.1_JR 0 -True G10 d01599_2010.04.20_1.1_NW/ 38.96 4.03E0 0 +TRUE A5 PcYABMw4iIW4wS57qORr1Z 15.31 1.50E+07 0 +TRUE A6 nCmYQbLEikiQ73iDxmY33Y 18.66 1.60E+06 0 +TRUE A7 R9xM.9TriuaExOpndyGo2X 22.16 1.55E+05 0 ? - Detector Call uncertain +TRUE A8 J5gXlHcYh1WqFKXNp3,j0o 25.7 1.46E+04 0 +TRUE A9 NTmiKt6kg3i0f4dB2VuZ3H 29.07 1.53E+03 0 +TRUE A10 ERMObpbGgJ,NIB4RV9Uk2v 32.51 1.54E+02 0 +TRUE A11 GVv2wR90hyyWrWmlYHZQ1C 36.35 1.19E+01 0 +TRUE A12 qSuSKy5.guyeUO2Qgb5K1M 37.45 5.69E+00 0 +TRUE B1 fg2aEmoaic,ykvyNBTcA3H 15.23 1.58E+07 0 +TRUE B2 EjGJbY7OisCA6mgJ8Tuv02 18.76 1.50E+06 0 +TRUE B3 zvkVnRFuhkWAa2QvLZXz2G 22.21 1.50E+05 0 +TRUE B4 ahMiyqLBiduSCJQ,Ehvf3Z 25.66 1.50E+04 0 +TRUE B5 U4zP.ntYjPOQ4BLo73jQ0B 29.31 1.31E+03 0 +TRUE B6 .sXZMhnmhG6pQ2DT,szM24 33.12 1.03E+02 0 +TRUE B7 5jgf,BCJikupRYPZPjhZ2b 35.37 2.28E+01 0 +TRUE B8 W,jAOvAGg7aScyq6H9jM2m 39.13 1.85E+00 0 +TRUE B9 ZlRXVQiihbGVVOGc0loj1L 24.75 2.75E+04 0 +TRUE B10 9cnm2nmRgyi9bAwqTcKR0j 24.7 2.84E+04 0 +TRUE B11 UmLWM2r2gAuu8chpTLos3D 24.67 2.90E+04 0 +TRUE B12 15I4KsOFiTakzH0pEer10y 35.85 3.01E+01 0 +TRUE C1 S413KTjZgz6whm2qrMPb16 25.49 3.96E+04 0 +TRUE C2 9fDACvZmiiawEJ1jqIxG12 26.52 1.94E+04 0 ? - Detector Call uncertain +TRUE C3 vGh29D5EhqmtFoI5Xuwp29 24.62 3.00E+04 0 +TRUE C4 rb65ZtanjGyIQexsQURK0z 24.7 2.84E+04 0 +TRUE C5 MvZpL,0sjKSoG8yshMBz2y 24.61 3.02E+04 0 +TRUE C6 VFb4BbLnh0iTRH2rpWfS0. 31.77 5.11E+02 0 +TRUE C7 SG1zaeXmj9WuXk0J.0t52H 15.31 1.50E+07 0 +TRUE C8 iAJncCq8htifqllq2oSW1p 18.66 1.60E+06 0 +TRUE C9 Hz.uBSS0jda0zvDBO,Ne05 22.16 1.55E+05 0 +TRUE C10 Joj43YcejVy9R9yQbjbd1a 25.7 1.46E+04 0 +TRUE C11 FFhmfuRegiOyv0fvAfGV0h 29.07 1.53E+03 0 +TRUE C12 jlairkLYgmyfD2,xS3t92r 32.51 1.54E+02 0 +TRUE D1 zL54,4JAgDusizgAY2TN31 36.35 1.19E+01 0 +TRUE D2 ODw7ggxfhGe9cVYhk4rZ0. 37.45 5.69E+00 0 +TRUE D3 9lNedv4JjcudaVd5mox83k 15.23 1.58E+07 0 +TRUE D4 RC6Nkl2Agribusk3rzSg13 18.76 1.50E+06 0 +TRUE D5 Fgc9kM8FhjifV1dBH7u114 22.21 1.50E+05 0 +TRUE D6 VlN0TAQFjGmvoejWpbXh1C 25.66 1.50E+04 0 +TRUE D7 SQOe,ooGhaK1LllIBMNo3, 29.31 1.31E+03 0 +TRUE D8 VtEwkC,2hvKw,zkd4z3N1H 33.12 1.03E+02 0 +TRUE D9 bGDfZMoMi1,IzuN,T7hI18 35.37 2.28E+01 0 +TRUE D10 tokW.oLsjzOsJOPx,qfm3s 39.13 1.85E+00 0 +TRUE D11 h8F3yj7viwO6l5IaHNzD1l 24.75 2.75E+04 0 +TRUE D12 NUgvOzGSgsyngjTB.2.n1u 24.7 2.84E+04 0 +TRUE E1 5cJ9wzBmhYi0ZQYWHpge20 24.67 2.90E+04 0 +TRUE E2 ouSpnxnugyaHWWrGibR43E 35.85 3.01E+01 0 ? - Detector Call uncertain +TRUE E3 TaVdU,q1gsabUki,t8fS0L 25.49 3.96E+04 0 +TRUE E4 fgSWPr3,gmyy0tv1gfSL2t 26.52 1.94E+04 0 +TRUE E5 uF5NiJx7ieidyTcftw,D1C 24.62 3.00E+04 0 +TRUE E6 vzeLEWw,ifmF9.GBUIyX3W 24.7 2.84E+04 0 +TRUE E7 BX07u8FKjuenFXD1w7c51O 24.61 3.02E+04 0 +TRUE E8 38u0EUMgihyOm.o811qu2b 31.77 5.11E+02 0 +TRUE E9 IGb9Rh0Ki12.cZtE78xF1T 15.31 1.50E+07 0 +TRUE E10 xseeKjrPgkSBagv.I4nT2f 18.66 1.60E+06 0 +TRUE E11 irLzykuTi1GrIuVFislQ1P 22.16 1.55E+05 0 +TRUE E12 wDMKC.nYhd,OCyZJv7.20E 25.7 1.46E+04 0 +TRUE F1 I7S7yF.sidekqOA5WRw.2q 29.07 1.53E+03 0 +TRUE F2 kgAtvMSvhZqbgY4uRNlN1J 32.51 1.54E+02 0 +TRUE F3 CL5KMUsIhKqe4f8k6mQo1W 36.35 1.19E+01 0 +TRUE F4 hXT2dz6widKRxPClPgj52N 37.45 5.69E+00 0 +TRUE F5 Gu70UnuzgruOXVz51pBD0H 15.23 1.58E+07 0 +TRUE F6 3KzBTqvLgu2aU.UWBO0Q1l 18.76 1.50E+06 0 +TRUE F7 JA0WvgUrhxWD2pu9rc1q0H 22.21 1.50E+05 0 +TRUE F8 LXqodPBpjDme2MZ.TIDG2X 25.66 1.50E+04 0 +TRUE F9 0f7BlbOkh5SopKJ4BL4,05 29.31 1.31E+03 0 ? - Detector Call uncertain +TRUE F10 YRL,Oec8iducoKOB1lOd3C 33.12 1.03E+02 0 +TRUE F11 Rk1oO4cjgoiCTYhCui3z2x 35.37 2.28E+01 0 +TRUE F12 1Uwe6MtZhYS78IFiKfc12F 39.13 1.85E+00 0 +TRUE G1 mucqgx5xiBO8H5WTFmXm2v 24.75 2.75E+04 0 +TRUE G2 qhPwle6DidKxz5yw03aM1a 24.7 2.84E+04 0 +TRUE G3 oe8u73LVjqi4gJbPKtLz2d 24.67 2.90E+04 1000000 +TRUE G4 T3isWmEPjZOzyJDC,lba25 35.85 3.01E+01 1000000 +TRUE G5 6Cr3r9KuimKZKoONeDe71s 25.49 3.96E+04 320000 +TRUE G6 fyjFvEWhhR,TYxmL0kWV0S 26.52 1.94E+04 320000 +TRUE G7 fZ.q9Y.FhCaR9JIdDTVR3I 24.62 3.00E+04 100000 +TRUE G8 Fsn74,wqjCeBwA5WLx,D1, 24.7 2.84E+04 100000 +TRUE G9 CdvGPxK2hFSuBFc7oHZw08 24.61 3.02E+04 32000 +TRUE G10 ZA1sM7aagR,uYt5mpXPL3E 31.77 5.11E+02 32000 +TRUE G11 QtbNbZGkiQOhV7Y3GQeM3T 15.31 1.50E+07 10000 +TRUE G12 nll3Z5XIhNWAYpDNj.oP1M 18.66 1.60E+06 10000 +TRUE H1 N4gjMYkjhtCjO.Pxd.Rg0E 22.16 1.55E+05 3200 +TRUE H2 112X8WV5gdWwUT5j6Ijh1m 25.7 1.46E+04 3200 +TRUE H3 kd6PdK1zjvuDsTabA24x3v 29.07 1.53E+03 1000 +TRUE H4 3N04G0esjUmQzkm40hBC2N 32.51 1.54E+02 1000 +TRUE H5 URDcemJIiR6HwFWLxo,g0E 36.35 1.19E+01 320 +TRUE H6 S8I,CajEjM6Wds7vJAX824 37.45 5.69E+00 320 +TRUE H7 pY1ISFpNhE,YaS.DSzTQ1Q 15.23 1.58E+07 100 +TRUE H8 7f8Xvcqhj0,1ZJ1dcQC01K 18.76 1.50E+06 100 ? - Detector Call uncertain +TRUE H9 5UdLi8bbhjCS3CLiYGkW3m 22.21 1.50E+05 32 +TRUE H10 EkzrU7hehnSYUMYhDDSc2G 25.66 1.50E+04 32 +TRUE H11 wUPszeEXiCCrnRpEtIbX3t 29.31 1.31E+03 10 +TRUE H12 u5OSfw3MjtqgWLbwhdhk3o 33.12 1.03E+02 10 \ No newline at end of file diff --git a/Viral_Load_Assay/resources/web/Viral_Load_Assay/SampleData/lc96.txt b/Viral_Load_Assay/resources/web/Viral_Load_Assay/SampleData/lc96.txt new file mode 100644 index 000000000..877b35d18 --- /dev/null +++ b/Viral_Load_Assay/resources/web/Viral_Load_Assay/SampleData/lc96.txt @@ -0,0 +1,93 @@ +Color Position Sample Name Gene Name Cq Concentration Call Excluded Sample Type Standard Cq Mean Cq Error Concentration Mean Concentration Error Replicate Group Dye Edited Call Slope EPF Failure Notes Sample Prep Notes Number +255;132;197;238 A5 PcYABMw4iIW4wS57qORr1Z None 15.31 1.50E+07 Positive Unchecked Unknown 1.50E+07 29.19 0.17 1.42E+03 1.61E+02 A5 FAM 0.09 0.58 None 1 +255;103;103;103 A6 nCmYQbLEikiQ73iDxmY33Y None 18.66 1.60E+06 Positive Unchecked Unknown 1.60E+06 32.82 0.43 1.28E+02 3.65E+01 A6 FAM 0.09 0.57 None 2 +255;242;138;21 A7 R9xM.9TriuaExOpndyGo2X None 22.16 1.55E+05 Positive Unchecked Unknown 1.55E+05 35.86 0.69 1.74E+01 7.76E+00 A7 FAM 0.08 0.5 None 3 +255;250;215;15 A8 J5gXlHcYh1WqFKXNp3,j0o None 25.7 1.46E+04 Negative Unchecked Unknown 1.46E+04 - - - - A8 FAM 0 0 Standard is negative 4 +255;103;154;47 A9 NTmiKt6kg3i0f4dB2VuZ3H None 29.07 1.53E+03 Positive Unchecked Unknown 1.53E+03 38.29 1.19 3.77E+00 2.72E+00 A9 FAM 0.09 0.56 None 5 +255;180;73;149 A10 ERMObpbGgJ,NIB4RV9Uk2v None 32.51 1.54E+02 Negative Unchecked Unknown 1.54E+02 - - - - A10 FAM 0 0.01 None 6 +255;95;103;174 A11 GVv2wR90hyyWrWmlYHZQ1C None 36.35 1.19E+01 Unknown 1.19E+01 7 +255;205;18;55 A12 qSuSKy5.guyeUO2Qgb5K1M None 37.45 5.69E+00 Unknown 5.69E+00 8 +255;34;176;146 B1 fg2aEmoaic,ykvyNBTcA3H None 15.23 1.58E+07 Positive Unchecked Unknown 1.58E+07 15.27 0.06 1.54E+07 5.83E+05 A1 FAM 0.09 0.57 None 9 +255;47;73;113 B2 EjGJbY7OisCA6mgJ8Tuv02 None 18.76 1.50E+06 Positive Unchecked Unknown 1.50E+06 18.71 0.07 1.55E+06 7.32E+04 A2 FAM 0.09 0.58 None 10 +255;132;197;238 B3 zvkVnRFuhkWAa2QvLZXz2G None 22.21 1.50E+05 Positive Unchecked Unknown 1.50E+05 22.19 0.04 1.52E+05 3.60E+03 A3 FAM 0.09 0.58 None 11 +255;103;103;103 B4 ahMiyqLBiduSCJQ,Ehvf3Z None 25.66 1.50E+04 Positive Unchecked Unknown 1.50E+04 25.68 0.03 1.48E+04 2.79E+02 A4 FAM 0.09 0.58 None 12 +255;242;138;21 B5 U4zP.ntYjPOQ4BLo73jQ0B None 29.31 1.31E+03 Positive Unchecked Unknown 1.31E+03 29.19 0.17 1.42E+03 1.61E+02 A5 FAM 0.09 0.58 None 13 +255;250;215;15 B6 .sXZMhnmhG6pQ2DT,szM24 None 33.12 1.03E+02 Positive Unchecked Unknown 1.03E+02 32.82 0.43 1.28E+02 3.65E+01 A6 FAM 0.09 0.56 None 14 +255;103;154;47 B7 5jgf,BCJikupRYPZPjhZ2b None 35.37 2.28E+01 Positive Unchecked Unknown 2.28E+01 35.86 0.69 1.74E+01 7.76E+00 A7 FAM 0.09 0.55 None 15 +255;180;73;149 B8 W,jAOvAGg7aScyq6H9jM2m None 39.13 1.85E+00 Negative Unchecked Unknown 1.85E+00 - - - - A8 FAM 0 0.01 Standard is negative 16 +255;95;103;174 B9 ZlRXVQiihbGVVOGc0loj1L None 24.75 2.75E+04 Positive Unchecked Unknown 2.75E+04 38.29 1.19 3.77E+00 2.72E+00 A9 FAM 0.09 0.53 None 17 +255;205;18;55 B10 9cnm2nmRgyi9bAwqTcKR0j None 24.7 2.84E+04 Negative Unchecked Unknown 2.84E+04 - - - - A10 FAM 0 0 None 18 +255;34;176;146 B11 UmLWM2r2gAuu8chpTLos3D None 24.67 2.90E+04 Unknown 2.90E+04 19 +255;47;73;113 B12 15I4KsOFiTakzH0pEer10y None 35.85 3.01E+01 Unknown 3.01E+01 20 +255;234;79;79 C1 S413KTjZgz6whm2qrMPb16 None 25.49 3.96E+04 Positive Unchecked Unknown 3.96E+04 24.69 0.09 2.87E+04 1.76E+03 C1 FAM 0.09 0.58 None 21 +255;66;41;24 C2 9fDACvZmiiawEJ1jqIxG12 None 26.52 1.94E+04 Positive Unchecked Unknown 1.94E+04 24.67 0.04 2.90E+04 8.29E+02 C2 FAM 0.09 0.57 None 22 +255;66;41;24 C3 vGh29D5EhqmtFoI5Xuwp29 None 24.62 3.00E+04 Positive Unchecked Unknown 3.00E+04 24.67 0.04 2.90E+04 8.29E+02 C2 FAM 0.09 0.57 None 23 +255;103;103;103 C4 rb65ZtanjGyIQexsQURK0z None 24.7 2.84E+04 Negative Unchecked Unknown 2.84E+04 - - - - C4 FAM 0 0.01 None 24 +255;242;138;21 C5 MvZpL,0sjKSoG8yshMBz2y None 24.61 3.02E+04 Negative Unchecked Unknown 3.02E+04 - - - - C5 FAM 0 0 None 25 +255;250;215;15 C6 VFb4BbLnh0iTRH2rpWfS0. None 31.77 5.11E+02 Negative Unchecked Unknown 5.11E+02 - - - - C6 FAM 0 0 None 26 +255;234;79;79 C7 SG1zaeXmj9WuXk0J.0t52H None 15.31 1.50E+07 Unknown 1.50E+07 15.27 0.06 1.54E+07 5.83E+05 A1 FAM 0.09 0.57 None 27 +255;66;41;24 C8 iAJncCq8htifqllq2oSW1p None 18.66 1.60E+06 Unknown 1.60E+06 18.71 0.07 1.55E+06 7.32E+04 A2 FAM 0.09 0.58 None 28 +255;66;41;24 C9 Hz.uBSS0jda0zvDBO,Ne05 None 22.16 1.55E+05 Unknown 1.55E+05 22.19 0.04 1.52E+05 3.60E+03 A3 FAM 0.09 0.58 None 29 +255;103;103;103 C10 Joj43YcejVy9R9yQbjbd1a None 25.7 1.46E+04 Unknown 1.46E+04 25.68 0.03 1.48E+04 2.79E+02 A4 FAM 0.09 0.58 None 30 +255;242;138;21 C11 FFhmfuRegiOyv0fvAfGV0h None 29.07 1.53E+03 Unknown 1.53E+03 29.19 0.17 1.42E+03 1.61E+02 A5 FAM 0.09 0.58 None 31 +255;250;215;15 C12 jlairkLYgmyfD2,xS3t92r None 32.51 1.54E+02 Unknown 1.54E+02 32.82 0.43 1.28E+02 3.65E+01 A6 FAM 0.09 0.56 None 32 +255;132;197;238 D1 zL54,4JAgDusizgAY2TN31 None 36.35 1.19E+01 Positive Unchecked Unknown 1.19E+01 35.86 0.69 1.74E+01 7.76E+00 A7 FAM 0.09 0.55 None 33 +255;103;103;103 D2 ODw7ggxfhGe9cVYhk4rZ0. None 37.45 5.69E+00 Positive Unchecked Unknown 5.69E+00 - - - - A8 FAM 0 0.01 Standard is negative 34 +255;242;138;21 D3 9lNedv4JjcudaVd5mox83k None 15.23 1.58E+07 Positive Unchecked Unknown 1.58E+07 38.29 1.19 3.77E+00 2.72E+00 A9 FAM 0.09 0.53 None 35 +255;250;215;15 D4 RC6Nkl2Agribusk3rzSg13 None 18.76 1.50E+06 Negative Unchecked Unknown 1.50E+06 - - - - A10 FAM 0 0 None 36 +255;103;154;47 D5 Fgc9kM8FhjifV1dBH7u114 None 22.21 1.50E+05 Negative Unchecked Unknown 1.50E+05 37 +255;180;73;149 D6 VlN0TAQFjGmvoejWpbXh1C None 25.66 1.50E+04 Negative Unchecked Unknown 1.50E+04 38 +255;95;103;174 D7 SQOe,ooGhaK1LllIBMNo3, None 29.31 1.31E+03 Unknown 1.31E+03 24.69 0.09 2.87E+04 1.76E+03 C1 FAM 0.09 0.58 None 39 +255;205;18;55 D8 VtEwkC,2hvKw,zkd4z3N1H None 33.12 1.03E+02 Unknown 1.03E+02 24.67 0.04 2.90E+04 8.29E+02 C2 FAM 0.09 0.57 None 40 +255;34;176;146 D9 bGDfZMoMi1,IzuN,T7hI18 None 35.37 2.28E+01 Unknown 2.28E+01 24.67 0.04 2.90E+04 8.29E+02 C2 FAM 0.09 0.57 None 41 +255;47;73;113 D10 tokW.oLsjzOsJOPx,qfm3s None 39.13 1.85E+00 Unknown 1.85E+00 - - - - C6 FAM 0 0 None 42 +255;132;197;238 D11 h8F3yj7viwO6l5IaHNzD1l None 24.75 2.75E+04 Unknown 2.75E+04 43 +255;103;103;103 D12 NUgvOzGSgsyngjTB.2.n1u None 24.7 2.84E+04 Unknown 2.84E+04 44 +255;242;138;21 E1 5cJ9wzBmhYi0ZQYWHpge20 None 24.67 2.90E+04 Unknown 2.90E+04 45 +255;250;215;15 E2 ouSpnxnugyaHWWrGibR43E None 35.85 3.01E+01 Unknown 3.01E+01 46 +255;103;154;47 E3 TaVdU,q1gsabUki,t8fS0L None 25.49 3.96E+04 Unknown 3.96E+04 15.27 0.06 1.54E+07 5.83E+05 A1 FAM 0.09 0.57 None 47 +255;180;73;149 E4 fgSWPr3,gmyy0tv1gfSL2t None 26.52 1.94E+04 Unknown 1.94E+04 18.71 0.07 1.55E+06 7.32E+04 A2 FAM 0.09 0.58 None 48 +255;95;103;174 E5 uF5NiJx7ieidyTcftw,D1C None 24.62 3.00E+04 Unknown 3.00E+04 22.19 0.04 1.52E+05 3.60E+03 A3 FAM 0.09 0.58 None 49 +255;205;18;55 E6 vzeLEWw,ifmF9.GBUIyX3W None 24.7 2.84E+04 Unknown 2.84E+04 25.68 0.03 1.48E+04 2.79E+02 A4 FAM 0.09 0.58 None 50 +255;34;176;146 E7 BX07u8FKjuenFXD1w7c51O None 24.61 3.02E+04 Unknown 3.02E+04 29.19 0.17 1.42E+03 1.61E+02 A5 FAM 0.09 0.58 None 51 +255;47;73;113 E8 38u0EUMgihyOm.o811qu2b None 31.77 5.11E+02 Unknown 5.11E+02 32.82 0.43 1.28E+02 3.65E+01 A6 FAM 0.09 0.56 None 52 +255;234;79;79 E9 IGb9Rh0Ki12.cZtE78xF1T None 15.31 1.50E+07 Unknown 1.50E+07 35.86 0.69 1.74E+01 7.76E+00 A7 FAM 0.09 0.55 None 53 +255;66;41;24 E10 xseeKjrPgkSBagv.I4nT2f None 18.66 1.60E+06 Unknown 1.60E+06 - - - - A8 FAM 0 0.01 Standard is negative 54 +255;66;41;24 E11 irLzykuTi1GrIuVFislQ1P None 22.16 1.55E+05 Unknown 1.55E+05 38.29 1.19 3.77E+00 2.72E+00 A9 FAM 0.09 0.53 None 55 +255;103;103;103 E12 wDMKC.nYhd,OCyZJv7.20E None 25.7 1.46E+04 Unknown 1.46E+04 - - - - A10 FAM 0 0 None 56 +255;242;138;21 F1 I7S7yF.sidekqOA5WRw.2q None 29.07 1.53E+03 Unknown 1.53E+03 57 +255;250;215;15 F2 kgAtvMSvhZqbgY4uRNlN1J None 32.51 1.54E+02 Unknown 1.54E+02 58 +255;234;79;79 F3 CL5KMUsIhKqe4f8k6mQo1W None 36.35 1.19E+01 Unknown 1.19E+01 24.69 0.09 2.87E+04 1.76E+03 C1 FAM 0.09 0.58 None 59 +255;66;41;24 F4 hXT2dz6widKRxPClPgj52N None 37.45 5.69E+00 Unknown 5.69E+00 24.67 0.04 2.90E+04 8.29E+02 C2 FAM 0.09 0.57 None 60 +255;66;41;24 F5 Gu70UnuzgruOXVz51pBD0H None 15.23 1.58E+07 Unknown 1.58E+07 24.67 0.04 2.90E+04 8.29E+02 C2 FAM 0.09 0.57 None 61 +255;103;103;103 F6 3KzBTqvLgu2aU.UWBO0Q1l None 18.76 1.50E+06 Unknown 1.50E+06 62 +255;242;138;21 F7 JA0WvgUrhxWD2pu9rc1q0H None 22.21 1.50E+05 Unknown 1.50E+05 63 +255;250;215;15 F8 LXqodPBpjDme2MZ.TIDG2X None 25.66 1.50E+04 Unknown 1.50E+04 24.69 0.09 2.87E+04 1.76E+03 C1 FAM 0.09 0.59 None 64 +255;132;197;238 F9 0f7BlbOkh5SopKJ4BL4,05 None 29.31 1.31E+03 Pos Control 1.31E+03 24.67 0.04 2.90E+04 8.29E+02 C2 FAM 0.09 0.58 None 65 +255;103;103;103 F10 YRL,Oec8iducoKOB1lOd3C None 33.12 1.03E+02 Pos Control 1.03E+02 24.67 0.04 2.90E+04 8.29E+02 C2 FAM 0.09 0.58 None 66 +255;242;138;21 F11 Rk1oO4cjgoiCTYhCui3z2x None 35.37 2.28E+01 Pos Control 2.28E+01 - - - - C4 FAM 0 0.01 None 67 +255;250;215;15 F12 1Uwe6MtZhYS78IFiKfc12F None 39.13 1.85E+00 Pos Control 1.85E+00 - - - - C5 FAM 0 0.01 None 68 +255;103;154;47 G1 mucqgx5xiBO8H5WTFmXm2v None 24.75 2.75E+04 Neg Control 2.75E+04 15.27 0.06 1.54E+07 5.83E+05 A1 FAM 0.09 0.57 None 69 +255;180;73;149 G2 qhPwle6DidKxz5yw03aM1a None 24.7 2.84E+04 Neg Control 2.84E+04 18.71 0.07 1.55E+06 7.32E+04 A2 FAM 0.09 0.58 None 70 +255;95;103;174 G3 oe8u73LVjqi4gJbPKtLz2d None 24.67 2.90E+04 Standard 2.90E+04 22.19 0.04 1.52E+05 3.60E+03 A3 FAM 0.09 0.58 None 71 +255;205;18;55 G4 T3isWmEPjZOzyJDC,lba25 None 35.85 3.01E+01 Standard 3.01E+01 25.68 0.03 1.48E+04 2.79E+02 A4 FAM 0.09 0.58 None 72 +255;34;176;146 G5 6Cr3r9KuimKZKoONeDe71s None 25.49 3.96E+04 Standard 3.96E+04 29.19 0.17 1.42E+03 1.61E+02 A5 FAM 0.09 0.58 None 73 +255;47;73;113 G6 fyjFvEWhhR,TYxmL0kWV0S None 26.52 1.94E+04 Standard 1.94E+04 32.82 0.43 1.28E+02 3.65E+01 A6 FAM 0.09 0.56 None 74 +255;132;197;238 G7 fZ.q9Y.FhCaR9JIdDTVR3I None 24.62 3.00E+04 Standard 3.00E+04 35.86 0.69 1.74E+01 7.76E+00 A7 FAM 0.09 0.55 None 75 +255;103;103;103 G8 Fsn74,wqjCeBwA5WLx,D1, None 24.7 2.84E+04 Standard 2.84E+04 - - - - A8 FAM 0 0.01 Standard is negative 76 +255;242;138;21 G9 CdvGPxK2hFSuBFc7oHZw08 None 24.61 3.02E+04 Standard 3.02E+04 38.29 1.19 3.77E+00 2.72E+00 A9 FAM 0.09 0.53 None 77 +255;250;215;15 G10 ZA1sM7aagR,uYt5mpXPL3E None 31.77 5.11E+02 Standard 5.11E+02 - - - - A10 FAM 0 0 None 78 +255;103;154;47 G11 QtbNbZGkiQOhV7Y3GQeM3T None 15.31 1.50E+07 Standard 1.50E+07 79 +255;180;73;149 G12 nll3Z5XIhNWAYpDNj.oP1M None 18.66 1.60E+06 Standard 1.60E+06 80 +255;95;103;174 H1 N4gjMYkjhtCjO.Pxd.Rg0E None 22.16 1.55E+05 Standard 1.55E+05 24.69 0.09 2.87E+04 1.76E+03 C1 FAM 0.09 0.58 None 81 +255;205;18;55 H2 112X8WV5gdWwUT5j6Ijh1m None 25.7 1.46E+04 Standard 1.46E+04 24.67 0.04 2.90E+04 8.29E+02 C2 FAM 0.09 0.57 None 82 +255;34;176;146 H3 kd6PdK1zjvuDsTabA24x3v None 29.07 1.53E+03 Standard 1.53E+03 24.67 0.04 2.90E+04 8.29E+02 C2 FAM 0.09 0.57 None 83 +255;47;73;113 H4 3N04G0esjUmQzkm40hBC2N None 32.51 1.54E+02 Standard 1.54E+02 24.69 0.09 2.87E+04 1.76E+03 C1 FAM 0.09 0.59 None 84 +255;234;79;79 H5 URDcemJIiR6HwFWLxo,g0E None 36.35 1.19E+01 Standard 1.19E+01 24.67 0.04 2.90E+04 8.29E+02 C2 FAM 0.09 0.58 None 85 +255;66;41;24 H6 S8I,CajEjM6Wds7vJAX824 None 37.45 5.69E+00 Standard 5.69E+00 24.67 0.04 2.90E+04 8.29E+02 C2 FAM 0.09 0.58 None 86 +255;95;103;174 H7 pY1ISFpNhE,YaS.DSzTQ1Q None 15.23 1.58E+07 Standard 1.58E+07 - - - - C4 FAM 0 0.01 None 87 +255;205;18;55 H8 7f8Xvcqhj0,1ZJ1dcQC01K None 18.76 1.50E+06 Standard 1.50E+06 - - - - C5 FAM 0 0.01 None 88 +255;34;176;146 H9 5UdLi8bbhjCS3CLiYGkW3m None 22.21 1.50E+05 Standard 1.50E+05 24.67 0.04 2.90E+04 8.29E+02 C2 FAM 0.09 0.57 None 89 +255;47;73;113 H10 EkzrU7hehnSYUMYhDDSc2G None 25.66 1.50E+04 Standard 1.50E+04 - - - - C4 FAM 0 0.01 None 90 +255;234;79;79 H11 wUPszeEXiCCrnRpEtIbX3t None 29.31 1.31E+03 Standard 1.31E+03 - - - - C5 FAM 0 0 None 91 +255;66;41;24 H12 u5OSfw3MjtqgWLbwhdhk3o None 33.12 1.03E+02 Standard 1.03E+02 - - - - C6 FAM 0 0 None 92 \ No newline at end of file diff --git a/Viral_Load_Assay/resources/web/Viral_Load_Assay/vl_utils.js b/Viral_Load_Assay/resources/web/Viral_Load_Assay/vl_utils.js new file mode 100644 index 000000000..9a407b21e --- /dev/null +++ b/Viral_Load_Assay/resources/web/Viral_Load_Assay/vl_utils.js @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2010-2012 LabKey Corporation + * + * Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0 + */ + +Ext4.namespace('ViralLoad.Utils'); + +ViralLoad.Utils = new function(){ + var lookup = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,.".split(""); + + return { + compressUUID: function (uuid) { + // Remove any hyphens + uuid = uuid.replace(/-/g, ""); + + var base16Triples = uuid.match(/.{1,3}/g); + + var base64Duples = base16Triples.map(function (base16Number) { + var integer = parseInt(base16Number, 16); + + var firstPart = Math.floor(integer / 64); + var secondPart = integer % 64; + + return "" + lookup[firstPart] + lookup[secondPart]; + }); + + return base64Duples.join(""); + }, + + uncompressUUID: function (compressedUUID) { + var base64Duples = compressedUUID.match(/.{1,2}/g); + var lastIndex = base64Duples.length - 1; + + var base16Duples = base64Duples.map(function (base64Set, curIndex) { + var firstPart = lookup.indexOf(base64Set.substr(0, 1)); + var secondPart = lookup.indexOf(base64Set.substr(1, 1)); + + var integer = (firstPart * 64) + secondPart; + + var hexString = integer.toString(16); + + if (hexString.length == 1) { + hexString = "00" + hexString; + } + else if (hexString.length == 2) { + hexString = "0" + hexString; + } + else if (hexString.length == 0) { + hexString = "000"; + } + + // The last piece is only 2 characters long. + if (curIndex == lastIndex) { + hexString = hexString.substr(1); + } + + return hexString.toLowerCase(); + }); + + var hexString = base16Duples.join(""); + + hexString = [hexString.substr(0, 8), hexString.substr(8, 4), hexString.substr(12, 4), hexString.substr(16, 4), hexString.substr(20, 12)].join("-"); + + return hexString; + }, + + testCompressUUID: function () { + var testUUID = function (UUID) { + if (!UUID) { + UUID = LABKEY.Utils.generateUUID(); + } + + return ( UUID == returnObj.uncompressUUID(returnObj.compressUUID(UUID)) ); + }; + + for (var i = 0; i < 100; i++) { + if (!testUUID()) { + return false; + } + } + + if (!testUUID("00000000-0000-0000-0000-000000000000")) { return false; } + if (!testUUID("ffffffff-ffff-ffff-ffff-ffffffffffff")) { return false; } + + return true; + } + } +} \ No newline at end of file diff --git a/Viral_Load_Assay/src/org/labkey/viral_load_assay/Viral_Load_AssayModule.java b/Viral_Load_Assay/src/org/labkey/viral_load_assay/Viral_Load_AssayModule.java index 3f22c6264..77cf7a5b5 100644 --- a/Viral_Load_Assay/src/org/labkey/viral_load_assay/Viral_Load_AssayModule.java +++ b/Viral_Load_Assay/src/org/labkey/viral_load_assay/Viral_Load_AssayModule.java @@ -49,7 +49,7 @@ public String getName() @Override public @Nullable Double getSchemaVersion() { - return 12.25; + return 12.28; } @Override diff --git a/Viral_Load_Assay/src/org/labkey/viral_load_assay/assay/AbstractWNPRCImportMethod.java b/Viral_Load_Assay/src/org/labkey/viral_load_assay/assay/AbstractWNPRCImportMethod.java new file mode 100644 index 000000000..ccbdfc5f1 --- /dev/null +++ b/Viral_Load_Assay/src/org/labkey/viral_load_assay/assay/AbstractWNPRCImportMethod.java @@ -0,0 +1,605 @@ +package org.labkey.viral_load_assay.assay; + +import org.apache.commons.beanutils.ConversionException; +import org.apache.commons.lang3.StringUtils; +import org.apache.log4j.Level; +import org.jetbrains.annotations.Nullable; +import org.json.JSONArray; +import org.json.JSONObject; +import org.labkey.api.collections.CaseInsensitiveHashMap; +import org.labkey.api.data.Container; +import org.labkey.api.data.ConvertHelper; +import org.labkey.api.data.Results; +import org.labkey.api.data.SimpleFilter; +import org.labkey.api.exp.api.ExpExperiment; +import org.labkey.api.exp.api.ExpProtocol; +import org.labkey.api.exp.api.ExpRun; +import org.labkey.api.laboratory.assay.AssayImportMethod; +import org.labkey.api.laboratory.assay.DefaultAssayParser; +import org.labkey.api.laboratory.assay.ImportContext; +import org.labkey.api.laboratory.assay.ParserErrors; +import org.labkey.api.query.BatchValidationException; +import org.labkey.api.query.FieldKey; +import org.labkey.api.query.QueryHelper; +import org.labkey.api.query.ValidationException; +import org.labkey.api.reader.TabLoader; +import org.labkey.api.security.User; +import org.labkey.api.util.Pair; +import org.labkey.api.util.ResultSetUtil; +import org.labkey.api.view.ViewContext; + +import java.io.File; +import java.io.IOException; +import java.sql.SQLException; +import java.text.SimpleDateFormat; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.HashSet; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; +import java.util.Set; + +public class AbstractWNPRCImportMethod extends DefaultVLImportMethod +{ + private static final String DATE_FIELD = "date"; + private static final String UNIQUE_FIELD = "uniqueSample"; + protected final static SimpleDateFormat _dateFormat = new SimpleDateFormat("yyyy-MM-dd"); + + //Formats that can be used for entering pooled sample dates + protected final static DateTimeFormatter[] _pooledDateFormats = { + DateTimeFormatter.ofPattern("yyyy-MM-dd"), + DateTimeFormatter.ofPattern("MM/dd/yyyy"), + DateTimeFormatter.ofPattern("MM-dd-yyyy"), + DateTimeFormatter.ofPattern("M/d/yyyy"), + DateTimeFormatter.ofPattern("M-d-yyyy") + }; + + public AbstractWNPRCImportMethod(String providerName) + { + super(providerName); + } + + @Override + public boolean hideTemplateDownload() { + return true; + } + + @Override + public boolean supportsRunTemplates() { + return true; + } + + @Override + public List getTemplateDownloadColumns() { + + //Set the columns that will be included in the excel file + //when downloading from the template (prepare run) page + ArrayList exportColumns = new ArrayList<>(); + exportColumns.add("uniqueSample"); + exportColumns.add("well"); + exportColumns.add("subjectId"); + exportColumns.add("date"); + + return exportColumns; + } + + @Override + public JSONObject getMetadata(ViewContext ctx, ExpProtocol protocol) + { + JSONObject meta = super.getMetadata(ctx, protocol); + + //Hide some fields that aren't used for these runs + JSONObject runMeta = meta.getJSONObject("Run"); + runMeta.put("slope", new JSONObject().put("hidden", true)); + runMeta.put("intercept", new JSONObject().put("hidden", true)); + runMeta.put("rSquared", new JSONObject().put("hidden", true)); + + JSONObject resultsMeta = meta.getJSONObject("Results"); + + JSONObject objectId = getJsonObject(resultsMeta, "objectid"); + JSONObject eluateMap = getJsonObject(resultsMeta, "eluateVol"); + JSONObject sourceMaterial = getJsonObject(resultsMeta, "sourceMaterial"); + + objectId.put("hidden", true); + eluateMap.put("defaultValue", 50); + eluateMap.put("setGlobally", true); + sourceMaterial.put("setGlobally", false); + + resultsMeta.put("objectid", objectId); + resultsMeta.put("eluateVol", eluateMap); + resultsMeta.put("sourceMaterial", sourceMaterial); + + meta.put("Run", runMeta); + meta.put("Results", resultsMeta); + + return meta; + } + + //Used to change metadata for the template (prepare run) page + @Override + public JSONObject getSupplementalTemplateMetadata() + { + JSONObject ret = new JSONObject(); + JSONObject domainMeta = new JSONObject(); + JSONObject resultsMeta = new JSONObject(); + + //Show the source material field as a column in the template table rather than + //globally in the result fields section + JSONObject sourceMaterial = getJsonObject(resultsMeta, "sourceMaterial"); + sourceMaterial.put("defaultValue", "Plasma"); + sourceMaterial.put("setGlobally", false); + sourceMaterial.put("hidden", false); + sourceMaterial.put("required", true); + resultsMeta.put("sourceMaterial", sourceMaterial); + + JSONObject sampleId = getJsonObject(resultsMeta, "sampleId"); + sampleId.put("hidden", true); + sampleId.put("required", false); + resultsMeta.put("sampleId", sampleId); + + //Show sample type globally in the result fields section rather than + //as a column in the template table + JSONObject sampleType = getJsonObject(resultsMeta, "sampleType"); + sampleType.put("setGlobally", true); + sampleType.put("hidden", false); + sampleType.put("required", true); + resultsMeta.put("sampleType", sampleType); + + //Whenever a new row is added to the template table automatically + //generate a new UUID for the unique sample column + JSONObject uniqueSample = getJsonObject(resultsMeta, "uniqueSample"); + uniqueSample.put("getInitialValue", "function(val) { return val || ViralLoad.Utils.compressUUID(LABKEY.Utils.generateUUID().toUpperCase()); }"); + resultsMeta.put("uniqueSample", uniqueSample); + + JSONObject units = getJsonObject(resultsMeta, "sourceMaterial/liquid"); + units.put("hidden", true); + units.put("required", false); + resultsMeta.put("sourceMaterial/liquid", units); + + /********************************************* + * Hide all of the new assay columns - Start * + *********************************************/ + + JSONObject color = getJsonObject(resultsMeta, "color"); + JSONObject geneName = getJsonObject(resultsMeta, "geneName"); + JSONObject call = getJsonObject(resultsMeta, "call"); + JSONObject excluded = getJsonObject(resultsMeta, "excluded"); + JSONObject cqMean = getJsonObject(resultsMeta, "cqMean"); + JSONObject cqError = getJsonObject(resultsMeta, "cqError"); + JSONObject concentrationMean = getJsonObject(resultsMeta, "concentrationMean"); + JSONObject concentrationError = getJsonObject(resultsMeta, "concentrationError"); + JSONObject replicateGroup = getJsonObject(resultsMeta, "replicateGroup"); + JSONObject dye = getJsonObject(resultsMeta, "dye"); + JSONObject editedCall = getJsonObject(resultsMeta, "editedCall"); + JSONObject slope = getJsonObject(resultsMeta, "slope"); + JSONObject epf = getJsonObject(resultsMeta, "epf"); + JSONObject failure = getJsonObject(resultsMeta, "failure"); + JSONObject notes = getJsonObject(resultsMeta, "notes"); + JSONObject samplePrepNotes = getJsonObject(resultsMeta, "samplePrepNotes"); + JSONObject number = getJsonObject(resultsMeta, "number"); + JSONObject qcstate = getJsonObject(resultsMeta, "qcstate"); + JSONObject qcresults = getJsonObject(resultsMeta, "qcresults"); + JSONObject pooled = getJsonObject(resultsMeta, "pooled"); + + color.put("hidden", true); + geneName.put("hidden", true); + call.put("hidden", true); + excluded.put("hidden", true); + cqMean.put("hidden", true); + cqError.put("hidden", true); + concentrationMean.put("hidden", true); + concentrationError.put("hidden", true); + replicateGroup.put("hidden", true); + dye.put("hidden", true); + editedCall.put("hidden", true); + slope.put("hidden", true); + epf.put("hidden", true); + failure.put("hidden", true); + notes.put("hidden", true); + samplePrepNotes.put("hidden", true); + number.put("hidden", true); + qcstate.put("hidden", true); + qcresults.put("hidden", true); + pooled.put("hidden", true); + + resultsMeta.put("color", color); + resultsMeta.put("geneName", geneName); + resultsMeta.put("call", call); + resultsMeta.put("excluded", excluded); + resultsMeta.put("cqMean", cqMean); + resultsMeta.put("cqError", cqError); + resultsMeta.put("concentrationMean", concentrationMean); + resultsMeta.put("concentrationError", concentrationError); + resultsMeta.put("replicateGroup", replicateGroup); + resultsMeta.put("dye", dye); + resultsMeta.put("editedCall", editedCall); + resultsMeta.put("slope", slope); + resultsMeta.put("epf", epf); + resultsMeta.put("failure", failure); + resultsMeta.put("notes", notes); + resultsMeta.put("samplePrepNotes", samplePrepNotes); + resultsMeta.put("number", number); + resultsMeta.put("qcstate", qcstate); + resultsMeta.put("qcresults", qcresults); + resultsMeta.put("pooled", pooled); + + /******************************************* + * Hide all of the new assay columns - End * + *******************************************/ + + domainMeta.put("Results", resultsMeta); + ret.put("domains", domainMeta); + + return ret; + } + + @Override + public void validateTemplate(User u, Container c, ExpProtocol protocol, @Nullable Integer templateId, String title, JSONObject json, BatchValidationException errors) throws BatchValidationException { + JSONObject resultDefaults = json.optJSONObject("Results"); + JSONArray rawResults = json.getJSONArray("ResultRows"); + Set distinctWells = new HashSet<>(); + + //Gets a mapping of all of the wells in the plate. In this case it should be A1 -> 1, A2 -> 2, ..., H12 -> 96 + Map wellMap = getWellMap96("well_96", "addressbyrow_96"); + + String[] requiredFields = new String[]{"well", "uniqueSample", "subjectId", "date", "sourceMaterial"}; + + //Queries to find all valid source materials (any material present in the viral_load_assay.source_material table) + List validSourceMaterials = new ArrayList<>(); + Results rs = null; + List column = new ArrayList<>(); + column.add(FieldKey.fromString("type")); + try { + while (c.isWorkbook()) { + c = c.getParent(); + } + QueryHelper qh = new QueryHelper(c, u, "viral_load_assay", "source_material"); + rs = qh.select(column, new SimpleFilter()); + + while (rs.next()) { + validSourceMaterials.add(rs.getString("type")); + } + } catch (SQLException e) { + //do nothing + } finally { + ResultSetUtil.close(rs); + } + + //Validate the contents of each template row + int rowIdx = 0; + for (JSONObject row : rawResults.toJSONObjectArray()) { + rowIdx++; + + //Override any defaults with actual user input + for (String prop : resultDefaults.keySet()) { + row.put(prop, resultDefaults.get(prop)); + } + + String[] ids = !StringUtils.isEmpty(row.getString("subjectId")) ? row.getString("subjectId").split(",\\s*") : new String[0]; + String[] pooledDates = !StringUtils.isEmpty(row.getString("pooledDates")) ? row.getString("pooledDates").split("[^0-9/-]") : new String[0]; + + //Check to make sure that all required fields are populated for the current row + boolean missingRequired = false; + for (String field : requiredFields) { + if (row.get(field) == null || StringUtils.isEmpty(row.getString(field))) { + if (field.equals(DATE_FIELD) && "Standard".equals(row.get("category"))) { + //Standard samples do not require a date + } else if (field.equals(DATE_FIELD) && pooledDates.length > 0) { + //This is a pooled sample and doesn't require a sampleDate as they'll all be in the pooledDates field + } else { + errors.addRowError(new ValidationException("Row " + rowIdx + ": missing required field: " + field)); + missingRequired = true; + } + } + } + + if (missingRequired) { + continue; + } + + //Check to make sure everything is formatted correctly if this is a pooled sample + if (ids.length > 1) { + if (StringUtils.isEmpty(row.getString(DATE_FIELD))) { + if (ids.length == pooledDates.length) { + //This is a pooled sample that is correctly formatted + + //validate the pooled dates (try 2 different common formats) + for (String pooledDate : pooledDates) { + if (parseDate(pooledDate) == null) { + errors.addRowError(new ValidationException("Row " + rowIdx + ": Invalid pooled date '" + pooledDate + "'")); + } + } + } else { + errors.addRowError(new ValidationException("Row " + rowIdx + ": number of subjects does not match number of pooled sample dates")); + } + } else { + errors.addRowError(new ValidationException("Row " + rowIdx + ": no sample date should be present for pooled samples")); + } + } else if (pooledDates.length > 0) { + errors.addRowError(new ValidationException("Row " + rowIdx + ": no pooled sample dates should be present when there is only a single subject id")); + } + + //Check to make sure that the source material matches one from the above query. As long + //as the spelling is the same (case can be different) it's fine. Use the casing from the + //source_material table when saving though. + String sourceMaterial = row.getString("sourceMaterial"); + boolean foundValidSourceMaterial = false; + for(String validSourceMaterial : validSourceMaterials) { + if(validSourceMaterial.equalsIgnoreCase(sourceMaterial)) { + row.put("sourceMaterial", validSourceMaterial); + foundValidSourceMaterial = true; + break; + } + } + if ("TestMaterial".equals(sourceMaterial)) { + foundValidSourceMaterial = true; + } + + if (!foundValidSourceMaterial) { + errors.addRowError(new ValidationException("Row " + rowIdx + ": " + sourceMaterial + " is not a valid source material")); + continue; + } + + String well = row.getString("well"); + if (well == null) { + errors.addRowError(new ValidationException("Row " + rowIdx + ": sample is missing well")); + continue; + } + + if (!wellMap.containsKey(well)) { + errors.addRowError(new ValidationException("Row " + rowIdx + ": unknown well: " + well)); + continue; + } + + if (distinctWells.contains(well)) { + errors.addRowError(new ValidationException("Row " + rowIdx + ": another sample is already present in well: " + well)); + continue; + } + distinctWells.add(well); + + String uniqueSample = row.getString("uniqueSample"); + if (uniqueSample == null) { + errors.addRowError(new ValidationException("Row " + rowIdx + ": sample is missing uniqueSample")); + continue; + } + + //validate the row's date + if (row.get(DATE_FIELD) != null) { + try { + Date date = ConvertHelper.convert(row.get(DATE_FIELD), Date.class); + _dateFormat.format(date); + } + catch (ConversionException | IllegalArgumentException e) { + errors.addRowError(new ValidationException("Row " + rowIdx + ": Invalid sample date")); + } + } + } + + super.validateTemplate(u, c, protocol, templateId, title, json, errors); + + if (errors.hasErrors()) { + throw errors; + } + } + + protected void calculateViralLoadForRoche(Map map) + { + //calculate VL + Double copiesPerRxn = map.get("Concentration") == null ? null : ((Number)map.get("Concentration")).doubleValue(); + map.put("copiesPerRxn", copiesPerRxn); + + Double eluateVol = new Double((Integer)map.get("eluateVol")); + Double volPerRxn = new Double((Integer)map.get("volPerRxn")); + if (!map.containsKey("sampleVol")) + map.put("sampleVol", 1.0); + + //This is the size (ml or mg) of the source material (plasma/serum/urine/etc.) + Double sampleVol = Double.parseDouble(map.get("sampleVol").toString()); + + Double viralLoad = 0.0; + Double dilutionFactor = 0.0; + if (copiesPerRxn != null && sampleVol > 0) + { + dilutionFactor = (1.0 / sampleVol) * (eluateVol / volPerRxn); + viralLoad = dilutionFactor * copiesPerRxn; + } + map.put("dilutionfactor", dilutionFactor); + map.put("viralLoad", viralLoad); + map.remove("Standard"); + map.remove("Concentration"); + } + + protected class Parser extends DefaultAssayParser { + private String HAS_RESULT = "__hasResult__"; + private int _assayId; + final String[] lookup = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,.".split(""); + //The expected number of columns in the import data + private int _expectedColumnCount = Integer.MAX_VALUE; + + public Parser(AssayImportMethod method, Container c, User u, int assayId, int resultsColumnCount) { + this(method, c, u ,assayId); + _expectedColumnCount = resultsColumnCount; + } + + public Parser(AssayImportMethod method, Container c, User u, int assayId) { + super(method, c, u, assayId); + _assayId = assayId; + } + + @Override + public Pair saveBatch(JSONObject json, File file, String fileName, ViewContext ctx) throws BatchValidationException { + int templateId = json.getInt("TemplateId"); + + Pair result = super.saveBatch(json, file, fileName, ctx); + + saveTemplate(ctx, templateId, result.second.getRowId()); + return result; + } + + //Needs to be overridden within the Parser class of each ImportMethod that extends AbstractWNPRCImportMethod + protected TabLoader getTabLoader(String contents) throws IOException { + return null; + } + + public int getAssayId() { + return _assayId; + } + + @Override + protected List> processRowsFromFile(List> rows, ImportContext context) throws BatchValidationException { + ParserErrors errors = context.getErrors(); + + String keyProperty = "uniqueSample"; + Map> templateRows = getTemplateRowMap(context, keyProperty); + + List> newRows = new ArrayList<>(); + ListIterator> rowsIter = rows.listIterator(); + int rowIdx = 0; + + //This means that the column headers were included either in the uploaded + //excel file, or when copy/pasting. Advance the iterator one position so + //that processing starts from the first row of actual data instead + if(templateRows.size() + 1 == rows.size()) { + if (rowsIter.hasNext()) { + rowsIter.next(); + } + } + while (rowsIter.hasNext()) { + rowIdx++; + Map row = rowsIter.next(); + Map map = new CaseInsensitiveHashMap<>(row); + + //Make sure that the expected number of columns are present in the data + if (row.size() != _expectedColumnCount) { + errors.addError("Improperly formatted row on line " + rowIdx + ", expected " + _expectedColumnCount + " cells"); + continue; + } + + appendPromotedResultFields(map, context); + + if (!map.containsKey(UNIQUE_FIELD) || map.get(UNIQUE_FIELD) == null || StringUtils.isEmpty((String) map.get(UNIQUE_FIELD))) { + errors.addError("Missing sample name for row: " + rowIdx); + continue; + } + if (map.get("well") != null) { + map.put("well", ((String) map.get("well")).toUpperCase()); + } + if (map.get("sampleType") == null) { + map.put("sampleType", "vRNA"); + } + + if (!mergeTemplateRow(keyProperty, templateRows, map, context)) { + continue; + } + + //these are not used + map.remove("Include"); + map.remove("Status"); + + if (map.get("uniqueSample") != null) { + map.put("objectid", uncompressUUID((String) map.get("uniqueSample"))); + } + + calculateViralLoadForRoche(map); + + //Check if it's a pooled sample and if it is split it out into multiple rows in the dataset + map.put("pooled", false); + String[] ids = !StringUtils.isEmpty((String) map.get("subjectId")) ? ((String) map.get("subjectId")).split(",\\s*") : new String[0]; + String[] pooledDates = !StringUtils.isEmpty((String) map.get("pooledDates")) ? ((String) map.get("pooledDates")).split("[^0-9/-]") : new String[0]; + for (int i = 0; i < ids.length; i++) { + Map newRow = new CaseInsensitiveHashMap<>(map); + if (ids.length > 1) { + String id = ids[i]; + LocalDate pooledDate = parseDate(pooledDates[i]); + newRow.put("subjectId", id); + newRow.put("date", pooledDate); + newRow.put("pooled", true); + } + newRows.add(newRow); + } + } + ensureTemplateRowsHaveResults(templateRows, context); + errors.confirmNoErrors(); + + return newRows; + } + + protected void ensureTemplateRowsHaveResults(Map> templateRows, ImportContext context) throws BatchValidationException { + for (String key : templateRows.keySet()) { + Map row = templateRows.get(key); + if (!row.containsKey(HAS_RESULT)) { + context.getErrors().addError("Template row with key " + key + " does not have a result", Level.WARN); + } + } + + context.getErrors().confirmNoErrors(); + } + + //Uncompresses the Base64 encoded uuid back to its original standard format + private String uncompressUUID(String compressObjectId) { + String reg = "(?<=\\G..)"; + String[] base64Duples = compressObjectId.split(reg); + int lastIndex = base64Duples.length - 1; + StringBuilder base16Duples = new StringBuilder(); + int position = 0; + for (String base64set : base64Duples) { + int firstPart = Arrays.asList(lookup).indexOf(base64set.substring(0, 1)); + int secondPart = Arrays.asList(lookup).indexOf(base64set.substring(1, 2)); + + int integer = (firstPart * 64) + secondPart; + + String hexString = Integer.toString(integer, 16); + + if (hexString.length() == 1) { + hexString = "00" + hexString; + } + else if (hexString.length() == 2) { + hexString = "0" + hexString; + } + else if (hexString.length() == 0) { + hexString = "000" + hexString; + } + + if (position == lastIndex) { + hexString = hexString.substring(1); + + } + base16Duples.append(hexString.toLowerCase()); + position++; + + } + char[] objectId = base16Duples.toString().toCharArray(); + + StringBuilder returnObjectId = new StringBuilder(); + + returnObjectId.append(objectId, 0, 8); + returnObjectId.append("-"); + returnObjectId.append(objectId, 8, 4); + returnObjectId.append("-"); + returnObjectId.append(objectId, 12, 4); + returnObjectId.append("-"); + returnObjectId.append(objectId, 16, 4); + returnObjectId.append("-"); + returnObjectId.append(objectId, 20, 12); + + return returnObjectId.toString(); + } + } + + //Check if the string is a date by checking a few different formats + private LocalDate parseDate(String date) { + for (DateTimeFormatter formatter : _pooledDateFormats) { + try { + return LocalDate.parse(date, formatter); + } catch (DateTimeParseException e) { + //ignore + } + } + return null; + } +} \ No newline at end of file diff --git a/Viral_Load_Assay/src/org/labkey/viral_load_assay/assay/LC480ImportMethod.java b/Viral_Load_Assay/src/org/labkey/viral_load_assay/assay/LC480ImportMethod.java index 53b15603f..477b6a329 100644 --- a/Viral_Load_Assay/src/org/labkey/viral_load_assay/assay/LC480ImportMethod.java +++ b/Viral_Load_Assay/src/org/labkey/viral_load_assay/assay/LC480ImportMethod.java @@ -15,26 +15,19 @@ */ package org.labkey.viral_load_assay.assay; -import org.apache.commons.lang3.StringUtils; import org.json.JSONObject; -import org.labkey.api.collections.CaseInsensitiveHashMap; import org.labkey.api.data.Container; import org.labkey.api.exp.api.ExpProtocol; import org.labkey.api.laboratory.assay.AssayImportMethod; import org.labkey.api.laboratory.assay.AssayParser; -import org.labkey.api.laboratory.assay.DefaultAssayParser; -import org.labkey.api.laboratory.assay.ImportContext; -import org.labkey.api.laboratory.assay.ParserErrors; -import org.labkey.api.query.BatchValidationException; -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.settings.AppProps; import org.labkey.api.view.ViewContext; -import java.util.ArrayList; -import java.util.List; -import java.util.ListIterator; -import java.util.Map; +import java.io.IOException; +import java.io.StringReader; /** * Created with IntelliJ IDEA. @@ -42,9 +35,10 @@ * Date: 9/15/12 * Time: 7:29 AM */ -public class LC480ImportMethod extends DefaultVLImportMethod +public class LC480ImportMethod extends AbstractWNPRCImportMethod { public static final String NAME = "LC480"; + public static final int RESULTS_COLUMN_COUNT = 7; public LC480ImportMethod(String providerName) { @@ -63,22 +57,16 @@ public String getLabel() return NAME; } - @Override - public boolean hideTemplateDownload() - { - return true; - } - @Override public String getTooltip() { - return "Choose this option to upload data directly from the output of a Roche LC480. NOTE: this expects the sample names to be formatted in a specific manner. Please see instructions above the reuslts section"; + return "Choose this option to upload data from the output of a Roche LC480. Please see instructions above the results section"; } @Override public String getTemplateInstructions() { - return "This is designed to accept the output directly from a Roche480 Light Cycler. However, in order for the results to be recognized by the system, the sample names must be formatted in a specific manner. This is: subject Id, undescore, sample date, underscore, plasma volume, underscore, comments. An example is: 'patient123_2010-03-04_1_Sample Run for Jim'. The comments and/or plasma volume can be ommited: 'patient123_2010-03-04'. If no plasma volume is provided, it will assume 1mL was used."; + return "This is designed to accept the output directly from a Roche480 Light Cycler. If using the Copy/Paste method be sure to copy all data, including the column headers."; } @Override @@ -102,89 +90,35 @@ public JSONObject getMetadata(ViewContext ctx, ExpProtocol protocol) runMeta.put("instrument", new JSONObject().put("defaultValue", "LC480")); meta.put("Run", runMeta); - JSONObject resultsMeta = meta.getJSONObject("Results"); - JSONObject eluateMap = getJsonObject(resultsMeta, "eluateVol"); - eluateMap.put("defaultValue", 50); - eluateMap.put("setGlobally", true); - resultsMeta.put("eluateVol", eluateMap); - - meta.put("Results", resultsMeta); return meta; } - private class Parser extends DefaultAssayParser + private class Parser extends AbstractWNPRCImportMethod.Parser { private static final String NAME_FIELD = "Name"; public Parser(AssayImportMethod method, Container c, User u, int assayId) { - super(method, c, u, assayId); + super(method, c, u, assayId, LC480ImportMethod.RESULTS_COLUMN_COUNT); } @Override - protected List> processRowsFromFile(List> rows, ImportContext context) throws BatchValidationException + protected TabLoader getTabLoader(String contents) throws IOException { - ParserErrors errors = context.getErrors(); - List> newRows = new ArrayList>(); - ListIterator> rowsIter = rows.listIterator(); - int rowIdx = 0; - while (rowsIter.hasNext()) - { - rowIdx++; - Map row = rowsIter.next(); - Map map = new CaseInsensitiveHashMap(row); - - if (row.size() < 6) - { - errors.addError("Improperly formatted row on line " + rowIdx); - continue; - } - appendPromotedResultFields(map, context); - - if (!map.containsKey("Name") || map.get("Name") == null) - { - errors.addError("Missing sample name for row: " + rowIdx); - continue; - } - - //these are not used - map.remove("Include"); - map.remove("Status"); - - //this is a slightly funny way to pass sample info, but it needs to be supported for legacy installs - //they name the samples using a specific format, which gets parsed here. If first token either matches the enum, - //then it is a standard or control otherwise we treat it as a general experimental sample. - String[] nameParts = StringUtils.split((String)map.get("Name"), "_"); - Category cat; - try - { - try - { - cat = Category.valueOf(nameParts[0]); - cat.parseSampleName(map, NAME_FIELD); - } - catch (IllegalArgumentException e) - { - Category.Unknown.parseSampleName(map, NAME_FIELD); - } - } - catch (ValidationException e) - { - errors.addError(e.getMessage()); - continue; - } - - map.put("well", map.get("Pos")); - map.remove("Pos"); - - calculateViralLoadForRoche(map); - - newRows.add(map); - } - - errors.confirmNoErrors(); - - return newRows; + TabLoader loader = new TabLoader(new StringReader(contents), false); + //These are the expected columns (in order) of the LC480 import data + ColumnDescriptor[] cols = new ColumnDescriptor[]{ + new ColumnDescriptor("Include", String.class), + new ColumnDescriptor("Pos", String.class), + new ColumnDescriptor("uniqueSample", String.class), + new ColumnDescriptor("Cp", Double.class), + new ColumnDescriptor("Concentration", Double.class), + new ColumnDescriptor("Standard", Integer.class), + new ColumnDescriptor("Status", String.class) + }; + + loader.setColumns(cols); + return loader; } } } diff --git a/Viral_Load_Assay/src/org/labkey/viral_load_assay/assay/LC96ImportMethod.java b/Viral_Load_Assay/src/org/labkey/viral_load_assay/assay/LC96ImportMethod.java new file mode 100644 index 000000000..ad87da2b8 --- /dev/null +++ b/Viral_Load_Assay/src/org/labkey/viral_load_assay/assay/LC96ImportMethod.java @@ -0,0 +1,120 @@ +/* + * Copyright (c) 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.viral_load_assay.assay; + +import org.json.JSONObject; +import org.labkey.api.data.Container; +import org.labkey.api.exp.api.ExpProtocol; +import org.labkey.api.laboratory.assay.AssayImportMethod; +import org.labkey.api.laboratory.assay.AssayParser; +import org.labkey.api.reader.ColumnDescriptor; +import org.labkey.api.reader.TabLoader; +import org.labkey.api.security.User; +import org.labkey.api.settings.AppProps; +import org.labkey.api.view.ViewContext; + +import java.io.IOException; +import java.io.StringReader; + +public class LC96ImportMethod extends AbstractWNPRCImportMethod { + public static final String NAME = "LC96"; + public static final int RESULTS_COLUMN_COUNT = 23; + + public LC96ImportMethod(String providerName) { + super(providerName); + } + + @Override + public String getName() { + return NAME; + } + + @Override + public String getLabel() { + return NAME; + } + + @Override + public String getTooltip() { + return "Choose this option to upload data from the output of a Roche LC96. Please see instructions above the results section"; + } + + @Override + public String getTemplateInstructions() { + return "This is designed to accept the output directly from a Roche96 Light Cycler. If using the Copy/Paste method be sure to copy all data, including the column headers."; + } + + public AssayParser getFileParser(Container c, User u, int assayId) { + return new Parser(this, c, u, assayId); + } + + @Override + public String getExampleDataUrl(ViewContext ctx) { + return AppProps.getInstance().getContextPath() + "/Viral_Load_Assay/SampleData/lc96.txt"; + } + + @Override + public JSONObject getMetadata(ViewContext ctx, ExpProtocol protocol) { + JSONObject meta = super.getMetadata(ctx, protocol); + + JSONObject runMeta = meta.getJSONObject("Run"); + runMeta.put("instrument", new JSONObject().put("defaultValue", "LC96")); + meta.put("Run", runMeta); + + return meta; + } + + private class Parser extends AbstractWNPRCImportMethod.Parser { + + public Parser(AssayImportMethod method, Container c, User u, int assayId) { + super(method, c, u, assayId, LC96ImportMethod.RESULTS_COLUMN_COUNT); + } + + @Override + protected TabLoader getTabLoader(String contents) throws IOException { + TabLoader loader = new TabLoader(new StringReader(contents), false); + //These are the expected columns (in order) of the LC96 import data + ColumnDescriptor[] cols = new ColumnDescriptor[]{ + new ColumnDescriptor("color", String.class), + new ColumnDescriptor("well", String.class), + new ColumnDescriptor("uniqueSample", String.class), + new ColumnDescriptor("geneName", String.class), + new ColumnDescriptor("Cp", Double.class), + new ColumnDescriptor("Concentration", Double.class), + new ColumnDescriptor("call", String.class), + new ColumnDescriptor("excluded", String.class), + new ColumnDescriptor("category", String.class), + new ColumnDescriptor("Standard", Integer.class), + new ColumnDescriptor("cqMean", Double.class), + new ColumnDescriptor("cqError", Double.class), + new ColumnDescriptor("concentrationMean", Double.class), + new ColumnDescriptor("concentrationError", Double.class), + new ColumnDescriptor("replicateGroup", String.class), + new ColumnDescriptor("dye", String.class), + new ColumnDescriptor("editedCall", String.class), + new ColumnDescriptor("slope", Double.class), + new ColumnDescriptor("epf", Double.class), + new ColumnDescriptor("failure", String.class), + new ColumnDescriptor("notes", String.class), + new ColumnDescriptor("samplePrepNotes", String.class), + new ColumnDescriptor("number", Integer.class) + }; + + loader.setColumns(cols); + return loader; + } + } +} diff --git a/Viral_Load_Assay/src/org/labkey/viral_load_assay/assay/LightCyclerImportMethod.java b/Viral_Load_Assay/src/org/labkey/viral_load_assay/assay/LightCyclerImportMethod.java deleted file mode 100644 index 12da08c3b..000000000 --- a/Viral_Load_Assay/src/org/labkey/viral_load_assay/assay/LightCyclerImportMethod.java +++ /dev/null @@ -1,206 +0,0 @@ -/* - * Copyright (c) 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.viral_load_assay.assay; - -import org.apache.commons.lang3.StringUtils; -import org.json.JSONObject; -import org.labkey.api.collections.CaseInsensitiveHashMap; -import org.labkey.api.data.Container; -import org.labkey.api.exp.api.ExpProtocol; -import org.labkey.api.laboratory.assay.AssayImportMethod; -import org.labkey.api.laboratory.assay.AssayParser; -import org.labkey.api.laboratory.assay.DefaultAssayParser; -import org.labkey.api.laboratory.assay.ImportContext; -import org.labkey.api.laboratory.assay.ParserErrors; -import org.labkey.api.query.BatchValidationException; -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.settings.AppProps; -import org.labkey.api.view.ViewContext; - -import java.io.IOException; -import java.io.StringReader; -import java.util.ArrayList; -import java.util.List; -import java.util.ListIterator; -import java.util.Map; - -/** - * Created with IntelliJ IDEA. - * User: bimber - * Date: 9/26/12 - * Time: 3:52 PM - */ -public class LightCyclerImportMethod extends LC480ImportMethod -{ - public static final String NAME = "Light Cycler"; - - public LightCyclerImportMethod(String providerName) - { - super(providerName); - } - - @Override - public String getName() - { - return NAME; - } - - @Override - public String getLabel() - { - return NAME; - } - - @Override - public boolean hideTemplateDownload() - { - return true; - } - - @Override - public String getTooltip() - { - return "Choose this option to upload data directly from the output of a Roche Light Cycle. NOTE: this expects the sample names to be formatted in a specific manner. Please see instructions above the results section"; - } - - @Override - public String getTemplateInstructions() - { - return "This is designed to accept the output directly from a Roche Light Cycler. However, in order for the results to be recognized by the system, the sample names must be formatted in a specific manner. This is: subject Id, undescore, sample date, underscore, plasma volume, underscore, comments. An example is: 'patient123_2010-03-04_1_Sample Run for Jim'. The comments and/or plasma volume can be ommited: 'patient123_2010-03-04'. If no plasma volume is provided, it will assume 1mL was used."; - } - - @Override - public AssayParser getFileParser(Container c, User u, int assayId) - { - return new Parser(this, c, u, assayId); - } - - @Override - public String getExampleDataUrl(ViewContext ctx) - { - return AppProps.getInstance().getContextPath() + "/Viral_Load_Assay/SampleData/LC_data.txt"; - } - - @Override - public JSONObject getMetadata(ViewContext ctx, ExpProtocol protocol) - { - JSONObject meta = super.getMetadata(ctx, protocol); - - JSONObject runMeta = meta.getJSONObject("Run"); - runMeta.put("instrument", new JSONObject().put("defaultValue", "Light Cycler")); - meta.put("Run", runMeta); - - return meta; - } - - private class Parser extends DefaultAssayParser - { - private static final String NAME_FIELD = "Name"; - - public Parser(AssayImportMethod method, Container c, User u, int assayId) - { - super(method, c, u, assayId); - } - - @Override - protected TabLoader getTabLoader(String contents) throws IOException - { - TabLoader loader = new TabLoader(new StringReader(contents), false); - ColumnDescriptor[] cols = new ColumnDescriptor[]{ - new ColumnDescriptor("Include", String.class), - new ColumnDescriptor("Pos", String.class), - new ColumnDescriptor("Name", String.class), - new ColumnDescriptor("Cp", Double.class), - new ColumnDescriptor("Concentration", Double.class), - new ColumnDescriptor("Standard", Integer.class), - new ColumnDescriptor("Status", String.class) - }; - - loader.setColumns(cols); - return loader; - } - - @Override - protected List> processRowsFromFile(List> rows, ImportContext context) throws BatchValidationException - { - ParserErrors errors = context.getErrors(); - - List> newRows = new ArrayList<>(); - ListIterator> rowsIter = rows.listIterator(); - int rowIdx = 0; - while (rowsIter.hasNext()) - { - rowIdx++; - Map row = rowsIter.next(); - Map map = new CaseInsensitiveHashMap<>(row); - - if (row.size() < 6) - { - errors.addError("Improperly formatted row on line " + rowIdx + ", expected 6 cells"); - continue; - } - - appendPromotedResultFields(map, context); - - if (!map.containsKey("Name") || map.get("Name") == null || StringUtils.isEmpty((String)map.get("Name"))) - { - errors.addError("Missing sample name for row: " + rowIdx); - continue; - } - - //these are not used - map.remove("Include"); - map.remove("Status"); - - //this is a slightly funny way to pass sample info, but it needs to be supported for legacy installs - //they name the samples using a specific format, which gets parsed here. If first token either matches the enum, - //then it is a standard or control otherwise we treat it as a general experimental sample. - String[] nameParts = StringUtils.split((String) map.get("Name"), "_"); - Category cat; - try - { - try - { - cat = Category.valueOf(nameParts[0]); - cat.parseSampleName(map, NAME_FIELD); - } - catch (IllegalArgumentException e) - { - Category.Unknown.parseSampleName(map, NAME_FIELD); - } - } - catch (ValidationException e) - { - errors.addError(e.getMessage()); - } - - map.put("well", map.get("Pos")); - map.remove("Pos"); - - calculateViralLoadForRoche(map); - - newRows.add(map); - } - - errors.confirmNoErrors(); - - return newRows; - } - } -} diff --git a/Viral_Load_Assay/src/org/labkey/viral_load_assay/assay/ViralLoadAssayDataProvider.java b/Viral_Load_Assay/src/org/labkey/viral_load_assay/assay/ViralLoadAssayDataProvider.java index e747f27ec..749d88eca 100644 --- a/Viral_Load_Assay/src/org/labkey/viral_load_assay/assay/ViralLoadAssayDataProvider.java +++ b/Viral_Load_Assay/src/org/labkey/viral_load_assay/assay/ViralLoadAssayDataProvider.java @@ -24,12 +24,15 @@ import org.labkey.api.module.Module; import org.labkey.api.security.User; import org.labkey.api.view.ViewContext; +import org.labkey.api.view.template.ClientDependency; import org.labkey.viral_load_assay.Viral_Load_AssayModule; import org.labkey.viral_load_assay.Viral_Load_Manager; import java.util.ArrayList; import java.util.Arrays; +import java.util.LinkedHashSet; import java.util.List; +import java.util.Set; /** * Created with IntelliJ IDEA. @@ -44,10 +47,10 @@ public ViralLoadAssayDataProvider(Module m) _providerName = Viral_Load_Manager.VL_ASSAY_PROVIDER_NAME; _module = m; + _importMethods.add(new ABI7500ImportMethod(_providerName)); _importMethods.add(new DefaultVLImportMethod(_providerName)); _importMethods.add(new LC480ImportMethod(_providerName)); - _importMethods.add(new LightCyclerImportMethod(_providerName)); - _importMethods.add(new ABI7500ImportMethod(_providerName)); + _importMethods.add(new LC96ImportMethod(_providerName)); } @Override @@ -68,7 +71,7 @@ public JSONObject getTemplateMetadata(ViewContext ctx) domainMeta.put("Run", runMeta); JSONObject resultMeta = getJsonObject(domainMeta, "Results"); - String[] hiddenResultFields = new String[]{"viralLoad", "viralLoadScientific", "copiesPerRxn", "cp", "qcflag", "requestid", "dilutionFactor", "Run", "sampleType", "eluateVol", "volPerRxn", "sourceMaterial"}; + String[] hiddenResultFields = new String[]{"viralLoad", "viralloadoorindicator", "viralLoadScientific", "copiesPerRxn", "cp", "qcflag", "requestid", "dilutionFactor", "Run", "sampleType", "eluateVol", "volPerRxn", "sourceMaterial"}; for (String field : hiddenResultFields) { JSONObject json = getJsonObject(resultMeta, field); @@ -120,4 +123,12 @@ public List getSettingsItems(Container c, User u) return items; } + + @Override + public Set getClientDependencies() + { + LinkedHashSet resources = new LinkedHashSet<>(); + resources.add(ClientDependency.fromPath("Viral_Load_Assay/vl_utils.js")); + return resources; + } } diff --git a/Viral_Load_Assay/test/src/org/labkey/test/tests/external/labModules/ViralLoadAssayTest.java b/Viral_Load_Assay/test/src/org/labkey/test/tests/external/labModules/ViralLoadAssayTest.java index 69123f9b6..4005c4b56 100644 --- a/Viral_Load_Assay/test/src/org/labkey/test/tests/external/labModules/ViralLoadAssayTest.java +++ b/Viral_Load_Assay/test/src/org/labkey/test/tests/external/labModules/ViralLoadAssayTest.java @@ -33,13 +33,18 @@ import org.labkey.test.categories.EHR; import org.labkey.test.categories.External; import org.labkey.test.categories.LabModule; +import org.labkey.test.components.domain.DomainFormPanel; +import org.labkey.test.pages.ReactAssayDesignerPage; +import org.labkey.test.params.FieldDefinition; import org.labkey.test.util.DataRegionTable; import org.labkey.test.util.Ext4Helper; import org.labkey.test.util.TextSearcher; +import org.labkey.test.util.UIAssayHelper; import org.labkey.test.util.ext4cmp.Ext4ComboRef; import org.labkey.test.util.ext4cmp.Ext4FieldRef; import org.labkey.test.util.ext4cmp.Ext4GridRef; import org.labkey.test.util.external.labModules.LabModuleHelper; +import org.openqa.selenium.By; import org.openqa.selenium.NoSuchElementException; import java.io.File; @@ -58,10 +63,12 @@ public class ViralLoadAssayTest extends AbstractLabModuleAssayTest { private static final String ASSAY_NAME = "Viral Load Test"; + private static final String LC480 = "LC480"; + private static final String LC96 = "LC96"; private String DETECTOR_NAME = "PIATAK SIVGAG"; private static final String[][] TEMPLATE_DATA = new String[][]{ - {"Well", "Subject Id", "Sample Date", "Category", "Sample Volume (mL)"}, + {"well", "subjectId", "date", "category", "sampleVol"}, {"A5", "LowQual1", "2012-02-01", "Unknown", "0.3"}, {"A6", "LowQual1", "2012-02-01", "Unknown", "0.3"}, {"A7", "LowQual2", "2012-06-01", "Unknown", "0.3"}, @@ -156,6 +163,102 @@ public class ViralLoadAssayTest extends AbstractLabModuleAssayTest {"H12", "STD_10", "", "Standard", "0.3"} }; + private static final String[][] WNPRC_TEMPLATE_DATA = new String[][]{ + {"well", "category", "subjectId", "date", "nucleicAcidVol", "sourceMaterial", "comment", "sampleVol", "uniqueSample"}, + {"A5", "Unknown", "LowQual1", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "PcYABMw4iIW4wS57qORr1Z"}, + {"A6", "Unknown", "LowQual1", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "nCmYQbLEikiQ73iDxmY33Y"}, + {"A7", "Unknown", "LowQual2", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "R9xM.9TriuaExOpndyGo2X"}, + {"A8", "Unknown", "LowQual2", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "J5gXlHcYh1WqFKXNp3,j0o"}, + {"A9", "Unknown", "Subject1", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "NTmiKt6kg3i0f4dB2VuZ3H"}, + {"A10", "Unknown", "Subject1", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "ERMObpbGgJ,NIB4RV9Uk2v"}, + {"A11", "Unknown", "Subject2", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "GVv2wR90hyyWrWmlYHZQ1C"}, + {"A12", "Unknown", "Subject2", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "qSuSKy5.guyeUO2Qgb5K1M"}, + {"B1", "Unknown", "Subject3", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "fg2aEmoaic,ykvyNBTcA3H"}, + {"B2", "Unknown", "Subject3", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "EjGJbY7OisCA6mgJ8Tuv02"}, + {"B3", "Unknown", "Subject4", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "zvkVnRFuhkWAa2QvLZXz2G"}, + {"B4", "Unknown", "Subject4", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "ahMiyqLBiduSCJQ,Ehvf3Z"}, + {"B5", "Unknown", "Subject5", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "U4zP.ntYjPOQ4BLo73jQ0B"}, + {"B6", "Unknown", "Subject5", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", ".sXZMhnmhG6pQ2DT,szM24"}, + {"B7", "Unknown", "Subject6", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "5jgf,BCJikupRYPZPjhZ2b"}, + {"B8", "Unknown", "Subject6", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "W,jAOvAGg7aScyq6H9jM2m"}, + {"B9", "Unknown", "Subject7", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "ZlRXVQiihbGVVOGc0loj1L"}, + {"B10", "Unknown", "Subject7", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "9cnm2nmRgyi9bAwqTcKR0j"}, + {"B11", "Unknown", "Subject8", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "UmLWM2r2gAuu8chpTLos3D"}, + {"B12", "Unknown", "Subject8", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "15I4KsOFiTakzH0pEer10y"}, + {"C1", "Unknown", "Subject9", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "S413KTjZgz6whm2qrMPb16"}, + {"C2", "Unknown", "Subject9", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "9fDACvZmiiawEJ1jqIxG12"}, + {"C3", "Unknown", "Subject10", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "vGh29D5EhqmtFoI5Xuwp29"}, + {"C4", "Unknown", "Subject10", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "rb65ZtanjGyIQexsQURK0z"}, + {"C5", "Unknown", "Subject11", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "MvZpL,0sjKSoG8yshMBz2y"}, + {"C6", "Unknown", "Subject11", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "VFb4BbLnh0iTRH2rpWfS0."}, + {"C7", "Unknown", "Subject12", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "SG1zaeXmj9WuXk0J.0t52H"}, + {"C8", "Unknown", "Subject12", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "iAJncCq8htifqllq2oSW1p"}, + {"C9", "Unknown", "Subject13", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "Hz.uBSS0jda0zvDBO,Ne05"}, + {"C10", "Unknown", "Subject13", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "Joj43YcejVy9R9yQbjbd1a"}, + {"C11", "Unknown", "Subject14", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "FFhmfuRegiOyv0fvAfGV0h"}, + {"C12", "Unknown", "Subject14", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "jlairkLYgmyfD2,xS3t92r"}, + {"D1", "Unknown", "Subject15", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "zL54,4JAgDusizgAY2TN31"}, + {"D2", "Unknown", "Subject15", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "ODw7ggxfhGe9cVYhk4rZ0."}, + {"D3", "Unknown", "Subject16", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "9lNedv4JjcudaVd5mox83k"}, + {"D4", "Unknown", "Subject16", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "RC6Nkl2Agribusk3rzSg13"}, + {"D5", "Unknown", "Subject17", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "Fgc9kM8FhjifV1dBH7u114"}, + {"D6", "Unknown", "Subject17", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "VlN0TAQFjGmvoejWpbXh1C"}, + {"D7", "Unknown", "Subject18", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "SQOe,ooGhaK1LllIBMNo3,"}, + {"D8", "Unknown", "Subject18", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "VtEwkC,2hvKw,zkd4z3N1H"}, + {"D9", "Unknown", "Subject19", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "bGDfZMoMi1,IzuN,T7hI18"}, + {"D10", "Unknown", "Subject19", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "tokW.oLsjzOsJOPx,qfm3s"}, + {"D11", "Unknown", "Subject20", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "h8F3yj7viwO6l5IaHNzD1l"}, + {"D12", "Unknown", "Subject20", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "NUgvOzGSgsyngjTB.2.n1u"}, + {"E1", "Unknown", "Subject21", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "5cJ9wzBmhYi0ZQYWHpge20"}, + {"E2", "Unknown", "Subject21", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "ouSpnxnugyaHWWrGibR43E"}, + {"E3", "Unknown", "Subject22", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "TaVdU,q1gsabUki,t8fS0L"}, + {"E4", "Unknown", "Subject22", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "fgSWPr3,gmyy0tv1gfSL2t"}, + {"E5", "Unknown", "Subject23", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "uF5NiJx7ieidyTcftw,D1C"}, + {"E6", "Unknown", "Subject23", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "vzeLEWw,ifmF9.GBUIyX3W"}, + {"E7", "Unknown", "Subject24", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "BX07u8FKjuenFXD1w7c51O"}, + {"E8", "Unknown", "Subject24", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "38u0EUMgihyOm.o811qu2b"}, + {"E9", "Unknown", "Subject25", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "IGb9Rh0Ki12.cZtE78xF1T"}, + {"E10", "Unknown", "Subject25", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "xseeKjrPgkSBagv.I4nT2f"}, + {"E11", "Unknown", "Subject26", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "irLzykuTi1GrIuVFislQ1P"}, + {"E12", "Unknown", "Subject26", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "wDMKC.nYhd,OCyZJv7.20E"}, + {"F1", "Unknown", "Subject27", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "I7S7yF.sidekqOA5WRw.2q"}, + {"F2", "Unknown", "Subject27", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "kgAtvMSvhZqbgY4uRNlN1J"}, + {"F3", "Unknown", "Subject28", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "CL5KMUsIhKqe4f8k6mQo1W"}, + {"F4", "Unknown", "Subject28", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "hXT2dz6widKRxPClPgj52N"}, + {"F5", "Unknown", "Subject29", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "Gu70UnuzgruOXVz51pBD0H"}, + {"F6", "Unknown", "Subject29", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "3KzBTqvLgu2aU.UWBO0Q1l"}, + {"F7", "Unknown", "Subject30", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "JA0WvgUrhxWD2pu9rc1q0H"}, + {"F8", "Unknown", "Subject30", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "LXqodPBpjDme2MZ.TIDG2X"}, + {"F9", "Pos Control", "Positive Control-1", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "0f7BlbOkh5SopKJ4BL4,05"}, + {"F10", "Pos Control", "Positive Control-1", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "YRL,Oec8iducoKOB1lOd3C"}, + {"F11", "Pos Control", "Positive Control-2", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "Rk1oO4cjgoiCTYhCui3z2x"}, + {"F12", "Pos Control", "Positive Control-2", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "1Uwe6MtZhYS78IFiKfc12F"}, + {"G1", "Neg Control", "NTC", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "mucqgx5xiBO8H5WTFmXm2v"}, + {"G2", "Neg Control", "NTC", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "qhPwle6DidKxz5yw03aM1a"}, + {"G3", "Standard", "STD_1000000", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "oe8u73LVjqi4gJbPKtLz2d"}, + {"G4", "Standard", "STD_1000000", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "T3isWmEPjZOzyJDC,lba25"}, + {"G5", "Standard", "STD_320000", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "6Cr3r9KuimKZKoONeDe71s"}, + {"G6", "Standard", "STD_320000", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "fyjFvEWhhR,TYxmL0kWV0S"}, + {"G7", "Standard", "STD_100000", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "fZ.q9Y.FhCaR9JIdDTVR3I"}, + {"G8", "Standard", "STD_100000", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "Fsn74,wqjCeBwA5WLx,D1,"}, + {"G9", "Standard", "STD_32000", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "CdvGPxK2hFSuBFc7oHZw08"}, + {"G10", "Standard", "STD_32000", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "ZA1sM7aagR,uYt5mpXPL3E"}, + {"G11", "Standard", "STD_10000", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "QtbNbZGkiQOhV7Y3GQeM3T"}, + {"G12", "Standard", "STD_10000", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "nll3Z5XIhNWAYpDNj.oP1M"}, + {"H1", "Standard", "STD_3200", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "N4gjMYkjhtCjO.Pxd.Rg0E"}, + {"H2", "Standard", "STD_3200", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "112X8WV5gdWwUT5j6Ijh1m"}, + {"H3", "Standard", "STD_1000", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "kd6PdK1zjvuDsTabA24x3v"}, + {"H4", "Standard", "STD_1000", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "3N04G0esjUmQzkm40hBC2N"}, + {"H5", "Standard", "STD_320", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "URDcemJIiR6HwFWLxo,g0E"}, + {"H6", "Standard", "STD_320", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "S8I,CajEjM6Wds7vJAX824"}, + {"H7", "Standard", "STD_100", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "pY1ISFpNhE,YaS.DSzTQ1Q"}, + {"H8", "Standard", "STD_100", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "7f8Xvcqhj0,1ZJ1dcQC01K"}, + {"H9", "Standard", "STD_32", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "5UdLi8bbhjCS3CLiYGkW3m"}, + {"H10", "Standard", "STD_32", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "EkzrU7hehnSYUMYhDDSc2G"}, + {"H11", "Standard", "STD_10", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "wUPszeEXiCCrnRpEtIbX3t"}, + {"H12", "Standard", "STD_10", "2018-02-20", "5", "TestMaterial", "test comment", "0.3", "u5OSfw3MjtqgWLbwhdhk3o"} + }; + public ViralLoadAssayTest() { PROJECT_NAME = "VL_AssayVerifyProject" + TRICKY_CHARACTERS_FOR_PROJECT_NAMES; @@ -168,8 +271,10 @@ public void testSteps() throws Exception setUpTest(); createPlateTemplate(); importABI7500Results(); - importLightCyclerRun(); - importLC480Run(); + createWNPRCPlateTemplate(LC480); + importWNPRCResults(LC480); + createWNPRCPlateTemplate(LC96); + importWNPRCResults(LC96); } @Override @@ -178,6 +283,72 @@ protected void setUpTest() throws Exception super.setUpTest(); ensureABI7500Records(); + setUpLC480Assay(); + setUpLC96Assay(); + } + + private void setUpLC480Assay() throws Exception + { + Map resultFields = new HashMap<>(); + resultFields.put("uniqueSample", FieldDefinition.ColumnType.String); + resultFields.put("nucleicAcidVol", FieldDefinition.ColumnType.Decimal); + defineViralAssayWithAdditionalFields("Viral Loads", LC480 + " " + ASSAY_NAME, null, null, resultFields); + } + + public void defineViralAssayWithAdditionalFields(String provider, String label, Map batchFields, Map runFields, Map resultFields) + { + log("Defining a test assay at the project level"); + //define a new assay at the project level + //the pipeline must already be setup + goToProjectHome(); + + //copied from old test + goToManageAssays(); + ReactAssayDesignerPage designerPage = new UIAssayHelper(this).createAssayDesign(provider, label); + + addViralAssayFields(batchFields, designerPage.goToBatchFields()); + addViralAssayFields(runFields, designerPage.goToRunFields()); + // This assay uses "Result" instead of "Results" for its domain name + addViralAssayFields(resultFields, designerPage.expandFieldsPanel("Result")); + + designerPage.clickFinish(); + } + + private void addViralAssayFields(Map fields, DomainFormPanel section) + { + if (fields != null) + { + for (Map.Entry entry : fields.entrySet()) + { + section.addField(new FieldDefinition(entry.getKey(), entry.getValue())); + } + } + } + + private void setUpLC96Assay() + { + Map resultFields = new HashMap<>(); + resultFields.put("uniqueSample", FieldDefinition.ColumnType.String); + resultFields.put("nucleicAcidVol", FieldDefinition.ColumnType.Decimal); + resultFields.put("color", FieldDefinition.ColumnType.String); + resultFields.put("geneName", FieldDefinition.ColumnType.String); + resultFields.put("call", FieldDefinition.ColumnType.String); + resultFields.put("excluded", FieldDefinition.ColumnType.String); + resultFields.put("cqMean", FieldDefinition.ColumnType.Decimal); + resultFields.put("cqError", FieldDefinition.ColumnType.Decimal); + resultFields.put("concentrationMean", FieldDefinition.ColumnType.Decimal); + resultFields.put("concentrationError", FieldDefinition.ColumnType.Decimal); + resultFields.put("replicateGroup", FieldDefinition.ColumnType.String); + resultFields.put("dye", FieldDefinition.ColumnType.String); + resultFields.put("editedCall", FieldDefinition.ColumnType.String); + resultFields.put("slope", FieldDefinition.ColumnType.Decimal); + resultFields.put("epf", FieldDefinition.ColumnType.Decimal); + resultFields.put("failure", FieldDefinition.ColumnType.String); + resultFields.put("notes", FieldDefinition.ColumnType.String); + resultFields.put("samplePrepNotes", FieldDefinition.ColumnType.String); + resultFields.put("number", FieldDefinition.ColumnType.Integer); + + defineViralAssayWithAdditionalFields("Viral Loads", LC96 + " " + ASSAY_NAME, null, null, resultFields); } private void ensureABI7500Records() throws CommandException, IOException @@ -205,7 +376,7 @@ private void ensureABI7500Records() throws CommandException, IOException rowMap.put("reporter", "FAM"); insertCmd.addRow(rowMap); SaveRowsResponse saveResp = insertCmd.execute(cn, getProjectName()); - assertEquals("Prolem creating record", saveResp.getRowsAffected(), (long)1); + assertEquals("Problem creating record", saveResp.getRowsAffected(), (long)1); } else { @@ -360,6 +531,94 @@ private void createPlateTemplate() } } + private void createWNPRCPlateTemplate(String instrument) + { + String fullAssayName = instrument + " " + ASSAY_NAME; + _helper.goToLabHome(); + _helper.clickNavPanelItem(fullAssayName + ":", IMPORT_DATA_TEXT); + click(Ext4Helper.Locators.menuItem("Prepare Run")); + waitForElement(Ext4Helper.Locators.window(IMPORT_DATA_TEXT)); + waitAndClickAndWait(Ext4Helper.Locators.ext4Button("Submit")); + + List expectedCols = new ArrayList<>(); + expectedCols.add("well"); + expectedCols.add("category"); + expectedCols.add("subjectId"); + expectedCols.add("date"); + expectedCols.add("nucleicAcidVol"); + expectedCols.add("sourceMaterial"); + expectedCols.add("comment"); + expectedCols.add("sampleVol"); + expectedCols.add("uniqueSample"); + + Ext4ComboRef.waitForField(this, "Import Method"); + Ext4ComboRef importMethodField = Ext4ComboRef.getForLabel(this, "Import Method"); + importMethodField.clickTrigger(); + getDriver().findElement(By.xpath("//li[text()='" + instrument + "']")).click(); + + waitForElement(Locator.xpath("//span[contains(text(), 'Source Material') and contains(@class, 'x4-column-header-text')]")); //ensure grid loaded + _helper.addRecordsToAssayTemplate(WNPRC_TEMPLATE_DATA, expectedCols); + + waitAndClick(Ext4Helper.Locators.ext4Button("Plate Layout")); + waitForElement(Ext4Helper.Locators.window("Configure Plate")); + waitForText("Group By Category"); + Ext4FieldRef.getForLabel(this, "Group By Category").setChecked(true); + waitForText("Below are the sample categories"); + Ext4FieldRef ctlField = Ext4FieldRef.getForLabel(this, "Neg Control (2)"); + ctlField.setValue(8); //A8 + waitAndClick(Ext4Helper.Locators.ext4Button("Submit")); + assertAlert("Error: Neg Control conflicts with an existing sample in well: A8"); + + ctlField.setValue(73); //corresponds to G1 + waitAndClick(Ext4Helper.Locators.ext4Button("Submit")); + + waitForElement(_helper.getAssayWell("G1", LabModuleHelper.NEG_COLOR)); + assertElementPresent(_helper.getAssayWell("G1", LabModuleHelper.NEG_COLOR)); + assertElementPresent(_helper.getAssayWell("F12", LabModuleHelper.POS_COLOR)); + assertElementPresent(_helper.getAssayWell("B5", LabModuleHelper.UNKNOWN_COLOR)); + assertElementPresent(_helper.getAssayWell("G3", LabModuleHelper.STD_COLOR)); + assertElementPresent(_helper.getAssayWell("H12", LabModuleHelper.STD_COLOR)); + + Ext4FieldRef.getForLabel(this, "Run Name").setValue("TestRun"); + + waitAndClick(Ext4Helper.Locators.ext4ButtonEnabled("Save and Close")); + waitForText("Save Complete"); + waitAndClick(Ext4Helper.Locators.ext4Button("OK")); + + //verify template created + _helper.clickNavPanelItem(fullAssayName + ":", IMPORT_DATA_TEXT); + click(Ext4Helper.Locators.menuItem("View Planned Runs")); + + log("Reopening saved run plan"); + waitForElement(Locator.tagContainingText("h3", "Planned Assay Runs"), WAIT_FOR_PAGE); + DataRegionTable dr = new DataRegionTable("query", this); + + dr.clickEditRow(0); + + //ensure previous run loads correctly + waitForElement(_helper.getAssayWell("G1", LabModuleHelper.NEG_COLOR), WAIT_FOR_PAGE); + assertElementPresent(_helper.getAssayWell("G1", LabModuleHelper.NEG_COLOR)); + assertElementPresent(_helper.getAssayWell("B5", LabModuleHelper.UNKNOWN_COLOR)); + assertElementPresent(_helper.getAssayWell("G3", LabModuleHelper.STD_COLOR)); + assertElementPresent(_helper.getAssayWell("H12", LabModuleHelper.STD_COLOR)); + + //no duplicate wells allowed + Ext4GridRef grid = _ext4Helper.queryOne("grid", Ext4GridRef.class); + grid.setGridCell(1, "well", "H11"); + click(Ext4Helper.Locators.ext4Button("Save")); + waitForElement(Ext4Helper.Locators.window("Error")); + click(Ext4Helper.Locators.ext4Button("OK")); + assertTextPresent("another sample is already present in well: H11"); + grid.setGridCell(1, "well", "A5"); //restore original contents + + //save valid data + click(Ext4Helper.Locators.ext4Button("Save")); + waitAndClick(Ext4Helper.Locators.ext4Button("OK")); + assertTextNotPresent("Must provide at least 2 negative controls per run"); + + goToProjectHome(); + } + /** * Imports results for the run created using createPlateTemplate(). * Also verifies error messages and VL calculations @@ -532,23 +791,169 @@ private void importABI7500Results() throws Exception assertEquals("Run plan not marked completed", 0, dr2.getDataRowCount()); } - private void importLC480Run() throws Exception +//// private void importWNPRCResults(String instrument) throws Exception +//// { +//// String fullAssayName = instrument + " " + ASSAY_NAME; +//// log("Verifying " + instrument + " Import"); +//// _helper.goToLabHome(); +//// _helper.clickNavPanelItem(fullAssayName + ":", IMPORT_DATA_TEXT); +//// click(Ext4Helper.Locators.menuItem("View Planned Runs")); +//// +//// log("Entering results for saved run"); +//// waitForElement(Locator.tagContainingText("h3", "Planned Assay Runs"), WAIT_FOR_PAGE); +//// DataRegionTable templates = new DataRegionTable("query", this); +//// clickAndWait(Locator.linkWithText("Enter Results")); +//// +//// //use the same data included with this assay +//// Locator btn = Locator.linkContainingText("Download Example Data"); +//// waitForElement(btn); +//// +//// assertEquals("Incorrect value for field", instrument, Ext4FieldRef.getForLabel(this, "Instrument").getValue()); +//// assertEquals("Incorrect value for field", Long.valueOf(50), Ext4FieldRef.getForLabel(this, "Eluate Volume").getValue()); +//// assertEquals("Incorrect value for field", Long.valueOf(5), Ext4FieldRef.getForLabel(this, "Sample Vol Per Rxn").getValue()); +//// waitAndClick(btn); +//// +//// Ext4FieldRef textarea = _ext4Helper.queryOne("#fileContent", Ext4FieldRef.class); +//// String text = _helper.getExampleData(); +//// +//// log("Trying to save invalid data"); +//// String errorText = text.replace("NTmiKt6kg3i0f4dB2VuZ3H", "NTmiKt6kg3i0f4dB2VuZ3I"); +//// errorText = errorText.replace("nCmYQbLEikiQ73iDxmY33Y", ""); +//// textarea.setValue(errorText); +//// waitAndClick(Ext4Helper.Locators.ext4Button("Upload")); +//// waitForElement(Ext4Helper.Locators.window("Upload Failed")); +//// click(Ext4Helper.Locators.ext4Button("OK")); +//// assertTextPresent("There were errors in the upload", "Missing sample name for row: 17"); +//// +//// log("Saving valid data"); +//// textarea.setValue(text); +//// waitAndClick(Ext4Helper.Locators.ext4Button("Upload")); +//// waitForElement(Ext4Helper.Locators.window("Success")); +//// waitAndClickAndWait(Ext4Helper.Locators.ext4Button("OK")); +//// waitForText("Import Samples"); +//// +//// log("Verifying results"); +//// _helper.clickNavPanelItemAndWait(ASSAY_NAME + " Runs:", 1); +//// waitAndClickAndWait(Locator.linkContainingText("view results")); +//// +//// DataRegionTable results = new DataRegionTable("Data", this); +//// +//// int totalRows = 38; +//// Map expected = new HashMap<>(); +//// expected.put("STD_15000000", new String[]{"STD_15000000", "Standard", "", "176000000"}); +//// expected.put("STD_5", new String[]{"STD_5", "Standard", "", "56.5"}); +//// expected.put("STD_3", new String[]{"STD_3", "Standard", "", "30"}); +//// expected.put("STD_0", new String[]{"STD_0", "Standard", "", "0"}); +//// expected.put("sd0159", new String[]{"sd0159", "Unknown", "2010-04-19", "793000"}); +//// expected.put("sd0160", new String[]{"sd0160", "Unknown", "2010-04-19", "0"}); +//// expected.put("sd0338", new String[]{"sd0338", "Unknown", "2010-04-19", "0"}); +//// expected.put("sd0339", new String[]{"sd0339", "Unknown", "2010-04-19", "0"}); +//// expected.put("sd0340", new String[]{"sd0340", "Unknown", "2010-04-19", "0"}); +//// expected.put("sd0341", new String[]{"sd0341", "Unknown", "2010-04-19", "0"}); +//// expected.put("sd0345", new String[]{"sd0345", "Unknown", "2010-04-19", "0"}); +//// expected.put("sd0346", new String[]{"sd0346", "Unknown", "2010-04-19", "0"}); +//// expected.put("deAJ11", new String[]{"deAJ11", "Unknown", "2010-04-21", "0"}); +//// expected.put("d90480", new String[]{"d90480", "Unknown", "2010-04-21", "0"}); +//// expected.put("d95149", new String[]{"d95149", "Unknown", "2010-04-21", "12000"}); +//// expected.put("d96061", new String[]{"d96061", "Unknown", "2010-04-21", "0"}); +//// expected.put("d56053", new String[]{"d56053", "Unknown", "2010-04-21", "560000"}); +//// expected.put("d28016", new String[]{"d28016", "Unknown", "2010-04-21", "4150"}); +//// expected.put("d98037", new String[]{"d98037", "Unknown", "2010-04-21", "17300"}); +//// expected.put("d96006", new String[]{"d96006", "Unknown", "2010-04-21", "301"}); +//// expected.put("d95019", new String[]{"d95019", "Unknown", "2010-04-21", "396000"}); +//// expected.put("d04032", new String[]{"d04032", "Unknown", "2010-04-21", "194000"}); +//// expected.put("d03019", new String[]{"d03019", "Unknown", "2010-04-21", "0"}); +//// expected.put("CTL_negative", new String[]{"CTL_negative", "Control", "", "0"}); +//// expected.put("CTL_negative", new String[]{"CTL_negative", "Control", "", "0"}); +//// expected.put("CTL_d02507", new String[]{"CTL_d02507", "Control", "", "10220"}); +//// expected.put("CTL_negative", new String[]{"CTL_negative", "Control", "", "0"}); +//// expected.put("CTL_r02007", new String[]{"CTL_r02007", "Control", "", "46600"}); +//// expected.put("d02056", new String[]{"d02056", "Unknown", "2010-04-19", "102000"}); +//// expected.put("d03830", new String[]{"d03830", "Unknown", "2010-04-19", "8500"}); +//// expected.put("d04291", new String[]{"d04291", "Unknown", "2010-04-19", "252000"}); +//// expected.put("d03504", new String[]{"d03504", "Unknown", "2010-04-19", "171000"}); +//// expected.put("dh6U10", new String[]{"dh6U10", "Unknown", "2010-04-20", "1300"}); +//// expected.put("d95067", new String[]{"d95067", "Unknown", "2010-04-20", "39.04"}); +//// expected.put("d02088", new String[]{"d02088", "Unknown", "2010-04-20", "1200000"}); +//// expected.put("d03037", new String[]{"d03037", "Unknown", "2010-04-20", "143.3"}); +//// expected.put("d04145", new String[]{"d04145", "Unknown", "2010-04-20", "0"}); +//// expected.put("d01599", new String[]{"d01599", "Unknown", "2010-04-20", "36.64"}); +//// +//// verifyImportedVLs(totalRows, expected, results, new String[]{"Subject Id"}); +//// } +//// +// private void verifyImportedVLs(int totalRows, Map expected, DataRegionTable results, @Nullable String[] keyFields) throws Exception +// { +// assertEquals("Incorrect row count", totalRows, results.getDataRowCount()); +// waitForText("SIVmac239-Gag"); //proxy for DR load +// +// log("DataRegion column count was: " + results.getColumnCount()); +// +// //recreate the DR to see if this removes intermittent test failures +// results = new DataRegionTable(results.getDataRegionName(), this); +// +// log("DataRegion column count was: " + results.getColumnCount()); +// +// //recreate the DR to see if this removes intermittent test failures +// results = new DataRegionTable(results.getDataRegionName(), this); +// +// DecimalFormat formatter = new DecimalFormat("0.#E00"); +// int i = 0; +// while (i < totalRows) +// { +// String subjectId = results.getDataAsText(i, "Subject Id"); +// String vl = results.getDataAsText(i, "Viral Load"); +// String dateString = StringUtils.trimToNull(results.getDataAsText(i, "Sample Date")); +// String date = dateString == null ? null : _dateFormat.format(_dateTimeFormat.parse(dateString)); +// String category = results.getDataAsText(i, "Category"); +// String[] expectedVals; +// StringBuilder sb = new StringBuilder(); +// if (keyFields == null) +// { +// expectedVals = expected.get(String.valueOf(i)); +// } +// else +// { +// String delim = ""; +// for (String field : keyFields) +// { +// sb.append(delim).append(results.getDataAsText(i, field)); +// delim = "_"; +// } +// expectedVals = expected.get(sb.toString()); +// } +// assertNotNull("Unable to find expected values: " + sb.toString(), expectedVals); +// +// assertEquals("Incorrect subjectId on row: " + i, expectedVals[0], subjectId); +// assertEquals("Incorrect category on row: " + i, expectedVals[1], category); +// assertEquals("Incorrect sample date on row: " + i, StringUtils.trimToNull(expectedVals[2]), date); +// +// Double vl1 = Double.parseDouble(expectedVals[3]); +// String vlFormatted = formatter.format(vl1); +// assertEquals("Incorrect VL on row: " + i, vlFormatted, StringUtils.trimToNull(vl)); +// +// i++; +// } +// } + + private void importWNPRCResults(String instrument) throws Exception { - log("Verifying LC480 Import"); - _helper.goToAssayResultImport(ASSAY_NAME); + String fullAssayName = instrument + " " + ASSAY_NAME; + log("Verifying " + instrument + " Import"); + _helper.goToLabHome(); + _helper.clickNavPanelItem(fullAssayName + ":", IMPORT_DATA_TEXT); + click(Ext4Helper.Locators.menuItem("View Planned Runs")); - //a proxy for page loading - _helper.waitForField("Source Material"); + log("Entering results for saved run"); + waitForElement(Locator.tagContainingText("h3", "Planned Assay Runs"), WAIT_FOR_PAGE); + DataRegionTable templates = new DataRegionTable("query", this); + clickAndWait(Locator.linkWithText("Enter Results")); - //switch import method - Ext4FieldRef field = Ext4FieldRef.getForBoxLabel(this, "LC480"); - field.setChecked(true); + //use the same data included with this assay Locator btn = Locator.linkContainingText("Download Example Data"); waitForElement(btn); - Ext4FieldRef.getForLabel(this, "Run Description").setValue("Description"); - - assertEquals("Incorrect value for field", "LC480", Ext4FieldRef.getForLabel(this, "Instrument").getValue()); + assertEquals("Incorrect value for field", instrument, Ext4FieldRef.getForLabel(this, "Instrument").getValue()); assertEquals("Incorrect value for field", Long.valueOf(50), Ext4FieldRef.getForLabel(this, "Eluate Volume").getValue()); assertEquals("Incorrect value for field", Long.valueOf(5), Ext4FieldRef.getForLabel(this, "Sample Vol Per Rxn").getValue()); waitAndClick(btn); @@ -557,157 +962,137 @@ private void importLC480Run() throws Exception String text = _helper.getExampleData(); log("Trying to save invalid data"); - String errorText = text.replaceAll("\t35.85\t3.01E1\t0", ""); - errorText = errorText.replaceAll("d56053_2010.04.21_1_JBS", ""); + String errorText = text.replace("NTmiKt6kg3i0f4dB2VuZ3H", "NTmiKt6kg3i0f4dB2VuZ3I"); + errorText = errorText.replace("nCmYQbLEikiQ73iDxmY33Y", ""); textarea.setValue(errorText); - waitAndClick(Ext4Helper.Locators.ext4Button("Upload")); + waitAndClick(WAIT_FOR_PAGE, Ext4Helper.Locators.ext4Button("Upload"), 0); waitForElement(Ext4Helper.Locators.window("Upload Failed")); click(Ext4Helper.Locators.ext4Button("OK")); - assertTextPresent("There were errors in the upload", "Missing sample name for row: 17"); + assertTextPresent( + "There were errors in the upload", + "Missing sample name for row: 2", + "Unable to find sample information to match well: NTmiKt6kg3i0f4dB2VuZ3I", + "Template row with key NTmiKt6kg3i0f4dB2VuZ3H does not have a result", + "Template row with key nCmYQbLEikiQ73iDxmY33Y does not have a result"); log("Saving valid data"); textarea.setValue(text); waitAndClick(Ext4Helper.Locators.ext4Button("Upload")); waitForElement(Ext4Helper.Locators.window("Success")); - waitAndClickAndWait(Ext4Helper.Locators.ext4Button("OK")); + clickAndWait(Ext4Helper.Locators.ext4Button("OK")); waitForText("Import Samples"); log("Verifying results"); - _helper.clickNavPanelItemAndWait(ASSAY_NAME + " Runs:", 1); + _helper.clickNavPanelItemAndWait(fullAssayName + " Runs:", 1); waitAndClickAndWait(Locator.linkContainingText("view results")); DataRegionTable results = new DataRegionTable("Data", this); - int totalRows = 38; - Map expected = new HashMap<>(); - expected.put("STD_15000000", new String[]{"STD_15000000", "Standard", "", "176000000"}); - expected.put("STD_5", new String[]{"STD_5", "Standard", "", "56.5"}); - expected.put("STD_3", new String[]{"STD_3", "Standard", "", "30"}); - expected.put("STD_0", new String[]{"STD_0", "Standard", "", "0"}); - expected.put("sd0159", new String[]{"sd0159", "Unknown", "2010-04-19", "793000"}); - expected.put("sd0160", new String[]{"sd0160", "Unknown", "2010-04-19", "0"}); - expected.put("sd0338", new String[]{"sd0338", "Unknown", "2010-04-19", "0"}); - expected.put("sd0339", new String[]{"sd0339", "Unknown", "2010-04-19", "0"}); - expected.put("sd0340", new String[]{"sd0340", "Unknown", "2010-04-19", "0"}); - expected.put("sd0341", new String[]{"sd0341", "Unknown", "2010-04-19", "0"}); - expected.put("sd0345", new String[]{"sd0345", "Unknown", "2010-04-19", "0"}); - expected.put("sd0346", new String[]{"sd0346", "Unknown", "2010-04-19", "0"}); - expected.put("deAJ11", new String[]{"deAJ11", "Unknown", "2010-04-21", "0"}); - expected.put("d90480", new String[]{"d90480", "Unknown", "2010-04-21", "0"}); - expected.put("d95149", new String[]{"d95149", "Unknown", "2010-04-21", "12000"}); - expected.put("d96061", new String[]{"d96061", "Unknown", "2010-04-21", "0"}); - expected.put("d56053", new String[]{"d56053", "Unknown", "2010-04-21", "560000"}); - expected.put("d28016", new String[]{"d28016", "Unknown", "2010-04-21", "4150"}); - expected.put("d98037", new String[]{"d98037", "Unknown", "2010-04-21", "17300"}); - expected.put("d96006", new String[]{"d96006", "Unknown", "2010-04-21", "301"}); - expected.put("d95019", new String[]{"d95019", "Unknown", "2010-04-21", "396000"}); - expected.put("d04032", new String[]{"d04032", "Unknown", "2010-04-21", "194000"}); - expected.put("d03019", new String[]{"d03019", "Unknown", "2010-04-21", "0"}); - expected.put("CTL_negative", new String[]{"CTL_negative", "Control", "", "0"}); - expected.put("CTL_negative", new String[]{"CTL_negative", "Control", "", "0"}); - expected.put("CTL_d02507", new String[]{"CTL_d02507", "Control", "", "10220"}); - expected.put("CTL_negative", new String[]{"CTL_negative", "Control", "", "0"}); - expected.put("CTL_r02007", new String[]{"CTL_r02007", "Control", "", "46600"}); - expected.put("d02056", new String[]{"d02056", "Unknown", "2010-04-19", "102000"}); - expected.put("d03830", new String[]{"d03830", "Unknown", "2010-04-19", "8500"}); - expected.put("d04291", new String[]{"d04291", "Unknown", "2010-04-19", "252000"}); - expected.put("d03504", new String[]{"d03504", "Unknown", "2010-04-19", "171000"}); - expected.put("dh6U10", new String[]{"dh6U10", "Unknown", "2010-04-20", "1300"}); - expected.put("d95067", new String[]{"d95067", "Unknown", "2010-04-20", "39.04"}); - expected.put("d02088", new String[]{"d02088", "Unknown", "2010-04-20", "1200000"}); - expected.put("d03037", new String[]{"d03037", "Unknown", "2010-04-20", "143.3"}); - expected.put("d04145", new String[]{"d04145", "Unknown", "2010-04-20", "0"}); - expected.put("d01599", new String[]{"d01599", "Unknown", "2010-04-20", "36.64"}); - - verifyImportedVLs(totalRows, expected, results, new String[]{"Subject Id"}); - } - - private void importLightCyclerRun() throws Exception - { - log("Verifying Light Cycle Import"); - _helper.goToAssayResultImport(ASSAY_NAME); - - //a proxy for page loading - _helper.waitForField("Source Material"); - - //switch import method - Ext4FieldRef field = Ext4FieldRef.getForBoxLabel(this, "Light Cycler"); - field.setChecked(true); - Locator btn = Locator.linkContainingText("Download Example Data"); - waitForElement(btn); - - //set other field values - Ext4FieldRef.getForLabel(this, "Sample Type").setValue("Serum"); - assertEquals("Incorrect value for field", "Light Cycler", Ext4FieldRef.getForLabel(this, "Instrument").getValue()); - assertEquals("Incorrect value for field", Long.valueOf(50), Ext4FieldRef.getForLabel(this, "Eluate Volume").getValue()); - assertEquals("Incorrect value for field", Long.valueOf(5), Ext4FieldRef.getForLabel(this, "Sample Vol Per Rxn").getValue()); - waitAndClick(btn); - - Ext4FieldRef textarea = _ext4Helper.queryOne("#fileContent", Ext4FieldRef.class); - String text = _helper.getExampleData(); - - String errorText = text.replaceAll("CTL_negative", ""); - errorText = errorText.replaceAll("de0115_2008.09.08_1_JG\t\t\t0", ""); - textarea.setValue(errorText); - waitAndClick(Ext4Helper.Locators.ext4Button("Upload")); - waitForElement(Ext4Helper.Locators.window("Upload Failed")); - click(Ext4Helper.Locators.ext4Button("OK")); - assertTextPresent( - "There were errors in the upload", - "Missing sample name for row: 23", - "Missing sample name for row: 27"); + int totalRows = 92; + Map expected = new LinkedHashMap<>(); + expected.put("0", new String[]{"LowQual1", "Unknown", "2018-02-20", "5.E+08"}); + expected.put("1", new String[]{"LowQual1", "Unknown", "2018-02-20", "5.3E+07"}); + expected.put("2", new String[]{"LowQual2", "Unknown", "2018-02-20", "5.2E+06"}); + expected.put("3", new String[]{"LowQual2", "Unknown", "2018-02-20", "4.9E+05"}); + expected.put("4", new String[]{"NTC", "Neg Control", "2018-02-20", "9.2E+05"}); + expected.put("5", new String[]{"NTC", "Neg Control", "2018-02-20", "9.5E+05"}); + expected.put("6", new String[]{"Positive Control-1", "Pos Control", "2018-02-20", "4.4E+04"}); + expected.put("7", new String[]{"Positive Control-1", "Pos Control", "2018-02-20", "3.4E+03"}); + expected.put("8", new String[]{"Positive Control-2", "Pos Control", "2018-02-20", "7.6E+02"}); + expected.put("9", new String[]{"Positive Control-2", "Pos Control", "2018-02-20", "6.2E+01"}); + expected.put("10", new String[]{"STD_10", "Standard", "2018-02-20", "4.4E+04"}); + expected.put("11", new String[]{"STD_10", "Standard", "2018-02-20", "3.4E+03"}); + expected.put("12", new String[]{"STD_32", "Standard", "2018-02-20", "5.E+06"}); + expected.put("13", new String[]{"STD_32", "Standard", "2018-02-20", "5.E+05"}); + expected.put("14", new String[]{"STD_100", "Standard", "2018-02-20", "5.3E+08"}); + expected.put("15", new String[]{"STD_100", "Standard", "2018-02-20", "5.E+07"}); + expected.put("16", new String[]{"STD_320", "Standard", "2018-02-20", "4.E+02"}); + expected.put("17", new String[]{"STD_320", "Standard", "2018-02-20", "1.9E+02"}); + expected.put("18", new String[]{"STD_1000", "Standard", "2018-02-20", "5.1E+04"}); + expected.put("19", new String[]{"STD_1000", "Standard", "2018-02-20", "5.1E+03"}); + expected.put("20", new String[]{"STD_3200", "Standard", "2018-02-20", "5.2E+06"}); + expected.put("21", new String[]{"STD_3200", "Standard", "2018-02-20", "4.9E+05"}); + expected.put("22", new String[]{"STD_10000", "Standard", "2018-02-20", "5.E+08"}); + expected.put("23", new String[]{"STD_10000", "Standard", "2018-02-20", "5.3E+07"}); + expected.put("24", new String[]{"STD_32000", "Standard", "2018-02-20", "1.E+06"}); + expected.put("25", new String[]{"STD_32000", "Standard", "2018-02-20", "1.7E+04"}); + expected.put("26", new String[]{"STD_100000", "Standard", "2018-02-20", "1.E+06"}); + expected.put("27", new String[]{"STD_100000", "Standard", "2018-02-20", "9.5E+05"}); + expected.put("28", new String[]{"STD_320000", "Standard", "2018-02-20", "1.3E+06"}); + expected.put("29", new String[]{"STD_320000", "Standard", "2018-02-20", "6.5E+05"}); + expected.put("30", new String[]{"STD_1000000", "Standard", "2018-02-20", "9.7E+05"}); + expected.put("31", new String[]{"STD_1000000", "Standard", "2018-02-20", "1.E+03"}); + expected.put("32", new String[]{"Subject1", "Unknown", "2018-02-20", "5.1E+04"}); + expected.put("33", new String[]{"Subject1", "Unknown", "2018-02-20", "5.1E+03"}); + expected.put("34", new String[]{"Subject2", "Unknown", "2018-02-20", "4.E+02"}); + expected.put("35", new String[]{"Subject2", "Unknown", "2018-02-20", "1.9E+02"}); + expected.put("36", new String[]{"Subject3", "Unknown", "2018-02-20", "5.3E+08"}); + expected.put("37", new String[]{"Subject3", "Unknown", "2018-02-20", "5.E+07"}); + expected.put("38", new String[]{"Subject4", "Unknown", "2018-02-20", "5.E+06"}); + expected.put("39", new String[]{"Subject4", "Unknown", "2018-02-20", "5.E+05"}); + expected.put("40", new String[]{"Subject5", "Unknown", "2018-02-20", "4.4E+04"}); + expected.put("41", new String[]{"Subject5", "Unknown", "2018-02-20", "3.4E+03"}); + expected.put("42", new String[]{"Subject6", "Unknown", "2018-02-20", "7.6E+02"}); + expected.put("43", new String[]{"Subject6", "Unknown", "2018-02-20", "6.2E+01"}); + expected.put("44", new String[]{"Subject7", "Unknown", "2018-02-20", "9.2E+05"}); + expected.put("45", new String[]{"Subject7", "Unknown", "2018-02-20", "9.5E+05"}); + expected.put("46", new String[]{"Subject8", "Unknown", "2018-02-20", "9.7E+05"}); + expected.put("47", new String[]{"Subject8", "Unknown", "2018-02-20", "1.E+03"}); + expected.put("48", new String[]{"Subject9", "Unknown", "2018-02-20", "1.3E+06"}); + expected.put("49", new String[]{"Subject9", "Unknown", "2018-02-20", "6.5E+05"}); + expected.put("50", new String[]{"Subject10", "Unknown", "2018-02-20", "1.E+06"}); + expected.put("51", new String[]{"Subject10", "Unknown", "2018-02-20", "9.5E+05"}); + expected.put("52", new String[]{"Subject11", "Unknown", "2018-02-20", "1.E+06"}); + expected.put("53", new String[]{"Subject11", "Unknown", "2018-02-20", "1.7E+04"}); + expected.put("54", new String[]{"Subject12", "Unknown", "2018-02-20", "5.E+08"}); + expected.put("55", new String[]{"Subject12", "Unknown", "2018-02-20", "5.3E+07"}); + expected.put("56", new String[]{"Subject13", "Unknown", "2018-02-20", "5.2E+06"}); + expected.put("57", new String[]{"Subject13", "Unknown", "2018-02-20", "4.9E+05"}); + expected.put("58", new String[]{"Subject14", "Unknown", "2018-02-20", "5.1E+04"}); + expected.put("59", new String[]{"Subject14", "Unknown", "2018-02-20", "5.1E+03"}); + expected.put("60", new String[]{"Subject15", "Unknown", "2018-02-20", "4.E+02"}); + expected.put("61", new String[]{"Subject15", "Unknown", "2018-02-20", "1.9E+02"}); + expected.put("62", new String[]{"Subject16", "Unknown", "2018-02-20", "5.3E+08"}); + expected.put("63", new String[]{"Subject16", "Unknown", "2018-02-20", "5.E+07"}); + expected.put("64", new String[]{"Subject17", "Unknown", "2018-02-20", "5.E+06"}); + expected.put("65", new String[]{"Subject17", "Unknown", "2018-02-20", "5.E+05"}); + expected.put("66", new String[]{"Subject18", "Unknown", "2018-02-20", "4.4E+04"}); + expected.put("67", new String[]{"Subject18", "Unknown", "2018-02-20", "3.4E+03"}); + expected.put("68", new String[]{"Subject19", "Unknown", "2018-02-20", "7.6E+02"}); + expected.put("69", new String[]{"Subject19", "Unknown", "2018-02-20", "6.2E+01"}); + expected.put("70", new String[]{"Subject20", "Unknown", "2018-02-20", "9.2E+05"}); + expected.put("71", new String[]{"Subject20", "Unknown", "2018-02-20", "9.5E+05"}); + expected.put("72", new String[]{"Subject21", "Unknown", "2018-02-20", "9.7E+05"}); + expected.put("73", new String[]{"Subject21", "Unknown", "2018-02-20", "1.E+03"}); + expected.put("74", new String[]{"Subject22", "Unknown", "2018-02-20", "1.3E+06"}); + expected.put("75", new String[]{"Subject22", "Unknown", "2018-02-20", "6.5E+05"}); + expected.put("76", new String[]{"Subject23", "Unknown", "2018-02-20", "1.E+06"}); + expected.put("77", new String[]{"Subject23", "Unknown", "2018-02-20", "9.5E+05"}); + expected.put("78", new String[]{"Subject24", "Unknown", "2018-02-20", "1.E+06"}); + expected.put("79", new String[]{"Subject24", "Unknown", "2018-02-20", "1.7E+04"}); + expected.put("80", new String[]{"Subject25", "Unknown", "2018-02-20", "5.E+08"}); + expected.put("81", new String[]{"Subject25", "Unknown", "2018-02-20", "5.3E+07"}); + expected.put("82", new String[]{"Subject26", "Unknown", "2018-02-20", "5.2E+06"}); + expected.put("83", new String[]{"Subject26", "Unknown", "2018-02-20", "4.9E+05"}); + expected.put("84", new String[]{"Subject27", "Unknown", "2018-02-20", "5.1E+04"}); + expected.put("85", new String[]{"Subject27", "Unknown", "2018-02-20", "5.1E+03"}); + expected.put("86", new String[]{"Subject28", "Unknown", "2018-02-20", "4.E+02"}); + expected.put("87", new String[]{"Subject28", "Unknown", "2018-02-20", "1.9E+02"}); + expected.put("88", new String[]{"Subject29", "Unknown", "2018-02-20", "5.3E+08"}); + expected.put("89", new String[]{"Subject29", "Unknown", "2018-02-20", "5.E+07"}); + expected.put("90", new String[]{"Subject30", "Unknown", "2018-02-20", "5.E+06"}); + expected.put("91", new String[]{"Subject30", "Unknown", "2018-02-20", "5.E+05"}); - log("Saving valid data"); - textarea.setValue(text); - waitAndClick(Ext4Helper.Locators.ext4Button("Upload")); - waitForElement(Ext4Helper.Locators.window("Success")); - waitAndClickAndWait(Ext4Helper.Locators.ext4Button("OK")); - waitForText("Import Samples"); + verifyImportedVLs(totalRows, expected, results, null); - log("Verifying results"); - _helper.clickNavPanelItemAndWait(ASSAY_NAME + " Runs:", 1); - waitAndClickAndWait(Locator.linkContainingText("view results")); + log("verifying run plan marked as complete"); + _helper.goToLabHome(); + _helper.clickNavPanelItem(fullAssayName + ":", IMPORT_DATA_TEXT); + click(Ext4Helper.Locators.menuItem("View Planned Runs")); + waitForElement(Locator.tagContainingText("h3", "Planned Assay Runs"), WAIT_FOR_PAGE); - DataRegionTable results = new DataRegionTable("Data", this); - int totalRows = 28; - - Map expected = new HashMap<>(); - expected.put("CTL_negative", new String[]{"CTL_negative", "Control", "", "0"}); - expected.put("CTL_r02007", new String[]{"CTL_r02007", "Control", "", "85000"}); - expected.put("d02321", new String[]{"d02321", "Unknown", "2008-09-08", "3370000"}); - expected.put("d02589", new String[]{"d02589", "Unknown", "2008-09-08", "0"}); - expected.put("d03103", new String[]{"d03103", "Unknown", "2008-09-08", "0"}); - expected.put("d04061", new String[]{"d04061", "Unknown", "2008-09-08", "0"}); - expected.put("d05099", new String[]{"d05099", "Unknown", "2008-09-08", "0"}); - expected.put("d05114", new String[]{"d05114", "Unknown", "2008-09-08", "0"}); - expected.put("d06012", new String[]{"d06012", "Unknown", "2008-09-08", "14640000"}); - expected.put("d07020", new String[]{"d07020", "Unknown", "2008-09-08", "0"}); - expected.put("d29063", new String[]{"d29063", "Unknown", "2008-09-08", "0"}); - expected.put("d29112", new String[]{"d29112", "Unknown", "2008-09-08", "0"}); - expected.put("d45069", new String[]{"d45069", "Unknown", "2008-09-08", "0"}); - expected.put("de0114", new String[]{"de0114", "Unknown", "2008-09-08", "9320"}); - expected.put("de0115", new String[]{"de0115", "Unknown", "2008-09-08", "0"}); - expected.put("de0150", new String[]{"de0150", "Unknown", "2008-09-08", "1860"}); - expected.put("de0152", new String[]{"de0152", "Unknown", "2008-09-08", "308000"}); - expected.put("de0166", new String[]{"de0166", "Unknown", "2008-09-08", "503000"}); - expected.put("STD_0", new String[]{"STD_0", "Standard", "", "0"}); - expected.put("STD_1.5", new String[]{"STD_1.5", "Standard", "", "0"}); - expected.put("STD_15", new String[]{"STD_15", "Standard", "", "150"}); - expected.put("STD_150", new String[]{"STD_150", "Standard", "", "1410"}); - expected.put("STD_1500", new String[]{"STD_1500", "Standard", "", "15600"}); - expected.put("STD_15000", new String[]{"STD_15000", "Standard", "", "157000"}); - expected.put("STD_150000", new String[]{"STD_150000", "Standard", "", "1530000"}); - expected.put("STD_1500000", new String[]{"STD_1500000", "Standard", "", "14700000"}); - expected.put("STD_15000000", new String[]{"STD_15000000", "Standard", "", "147000000"}); - expected.put("STD_3", new String[]{"STD_3", "Standard", "", "0"}); - - waitForElement(Locator.linkContainingText("Control")); //proxy for DR load - results = new DataRegionTable(results.getDataRegionName(), this); //attempt to fix test timing - - String sampleType = results.getDataAsText(2, "Sample Type"); - assertEquals("Incorrect sample type", "Serum", sampleType); - - verifyImportedVLs(totalRows, expected, results, new String[]{"Subject Id"}); + DataRegionTable dr2 = new DataRegionTable("query", this); + assertEquals("Run plan not marked completed", 0, dr2.getDataRowCount()); } private void verifyImportedVLs(int totalRows, Map expected, DataRegionTable results, @Nullable String[] keyFields) throws Exception diff --git a/ehr/api-src/org/labkey/api/ehr/security/EHR_BillingAdminPermission.java b/ehr/api-src/org/labkey/api/ehr/security/EHR_BillingAdminPermission.java new file mode 100644 index 000000000..4c0469007 --- /dev/null +++ b/ehr/api-src/org/labkey/api/ehr/security/EHR_BillingAdminPermission.java @@ -0,0 +1,11 @@ +package org.labkey.api.ehr.security; + +import org.labkey.api.security.permissions.AbstractPermission; + +public class EHR_BillingAdminPermission extends AbstractPermission +{ + public EHR_BillingAdminPermission() + { + super("EHR_BillingAdminPermission", "Can insert and update data in the EHR Billing tables"); + } +} \ No newline at end of file diff --git a/ehr/resources/credits/scripts.txt b/ehr/resources/credits/scripts.txt deleted file mode 100644 index 1a38b43f8..000000000 --- a/ehr/resources/credits/scripts.txt +++ /dev/null @@ -1,9 +0,0 @@ -{table} -Library|Version|Source|License|LabKey Dev|Purpose -Knockout|3.3.0|{link:Knockout.js|http://knockoutjs.com}|{link:MIT|http://opensource.org/licenses/mit-license.php}|jrichardson|Lightweight, Responsive Components and Observables -Knockout-Mapping|2.4.1|{link:Knockout.js/Mapping Plugin|http://knockoutjs.com/documentation/plugins-mapping.html}|{link:MIT|http://opensource.org/licenses/mit-license.php}|jrichardson|Mapping Plugin for Mapping Objects to Observables -Underscore|1.8.3|{link:Underscore.js|http://underscorejs.org/}|{link:MIT|http://opensource.org/licenses/mit-license.php}|jrichardson|Lightweight Object/Array Utilities -Underscore-String|3.2.2|{link:Underscore.String.js|https://github.com/epeli/underscore.string}|{link:MIT|http://opensource.org/licenses/mit-license.php}|jrichardson|Lightweight String Utilities -Classify|0.0.1|{link:ClassifyJS|https://github.com/JonathonRichardson/ClassifyJS}|{link:Apache 2.0|https://github.com/JonathonRichardson/ClassifyJS/blob/master/LICENSE}|jrichardson|Class Structures -SuperSQLStore|0.0.1|{link:SuperSQLStore|https://github.com/JonathonRichardson/SuperSQLStoreJS}|{link:Apache 2.0|https://github.com/JonathonRichardson/SuperSQLStoreJS/blob/master/LICENSE}|jrichardson|Observable Tables to Bind to Knockout -{table} \ No newline at end of file diff --git a/ehr/resources/data/lookup_sets.tsv b/ehr/resources/data/lookup_sets.tsv index 61ee557e0..a3f1df29d 100644 --- a/ehr/resources/data/lookup_sets.tsv +++ b/ehr/resources/data/lookup_sets.tsv @@ -37,7 +37,8 @@ histology_stain Histology Stain Field Values value hold_codes Hold Codes Field Values value title housing_condition_codes Housing Condition Code Field Values value title housing_reason Housing Reason Field Values value -immunology_method Immunology Method Field Values value +immunology_method Immunology Method Field Values value +location_water Location Water Field Values value microchip_comments Microchip Comments Field Values value necropsy_condition Necropsy Condition Field Values value necropsy_perfusion Necropsy Perfusion Field Values value @@ -56,7 +57,8 @@ parasitology_method Parasitology Method Field Values value pe_region PE Region Field Values value pe_remarks PE Remarks Field Values value preservation_solutions Preservation Solution Field Values value -problem_list_category Problem List Category Field Values value +problem_list_category Problem List Category Field Values value +reason_food_deprive Reason For Deprive Field Values value restraint_duration Restraint Duration Field Values value sample_types Sample Type Field Values value snomed_qualifiers SNOMED Qualifiers Field Values value diff --git a/ehr/resources/module.xml b/ehr/resources/module.xml index 2839c1082..079e095fd 100644 --- a/ehr/resources/module.xml +++ b/ehr/resources/module.xml @@ -96,6 +96,5 @@ - diff --git a/ehr/resources/queries/core/QCState.query.xml b/ehr/resources/queries/core/QCState.query.xml index 734728dea..84a85e498 100644 --- a/ehr/resources/queries/core/QCState.query.xml +++ b/ehr/resources/queries/core/QCState.query.xml @@ -9,7 +9,6 @@ 50 - 80 true false diff --git a/ehr/resources/queries/ehr/protocol_title.sql b/ehr/resources/queries/ehr/protocol_title.sql new file mode 100644 index 000000000..dcc39c067 --- /dev/null +++ b/ehr/resources/queries/ehr/protocol_title.sql @@ -0,0 +1,2 @@ +SELECT protocol, title , protocol || '-' || coalesce (title,'No Title') as protocolTitle +FROM ehr.protocol \ No newline at end of file diff --git a/ehr/resources/queries/ehr/tasks.query.xml b/ehr/resources/queries/ehr/tasks.query.xml index 2cc725020..176a9e0d9 100644 --- a/ehr/resources/queries/ehr/tasks.query.xml +++ b/ehr/resources/queries/ehr/tasks.query.xml @@ -127,6 +127,12 @@ FBEC5D + + + + + FBEC5D + diff --git a/ehr/resources/queries/study/blood.js b/ehr/resources/queries/study/blood.js index 515935a30..98ee234c8 100644 --- a/ehr/resources/queries/study/blood.js +++ b/ehr/resources/queries/study/blood.js @@ -6,7 +6,7 @@ require("ehr/triggers").initScript(this); -EHR.Server.TriggerManager.registerHandlerForQuery(EHR.Server.TriggerManager.Events.AFTER_BECOME_PUBLIC, 'study', 'Blood Draws', function(errors, helper, row, oldRow){ +EHR.Server.TriggerManager.registerHandlerForQuery(EHR.Server.TriggerManager.Events.AFTER_BECOME_PUBLIC, 'study', 'blood', function(errors, helper, row, oldRow){ if (row && row.additionalServices){ helper.getJavaHelper().createRequestsForBloodAdditionalServices(row.Id, row.date, row.project, row.account, row.performedby, row.additionalServices, (row.requestid||null)); } diff --git a/ehr/resources/queries/study/colonyPopulationChange.sql b/ehr/resources/queries/study/colonyPopulationChange.sql index 1378540f9..a83267499 100644 --- a/ehr/resources/queries/study/colonyPopulationChange.sql +++ b/ehr/resources/queries/study/colonyPopulationChange.sql @@ -22,14 +22,13 @@ SELECT T2.id, -- T2.id.dataset.demographics.species, 'Arrivals' AS Category, - max(T2.date), - convert(year(max(T2.date)), INTEGER) AS Year, + T2.date, + convert(year(T2.date), INTEGER) AS Year, FROM study.Arrival T2 WHERE T2.date IS NOT NULL AND T2.qcstate.publicdata = true and cast(COALESCE(STARTDATE, '1900-01-01') as date) <= T2.date and cast(COALESCE(ENDDATE, curdate()) as date) >= cast(T2.date as date) -group by id UNION ALL @@ -37,14 +36,13 @@ SELECT T3.id, -- T3.id.dataset.demographics.species, 'Departures' AS Category, - max(T3.Date), - convert(year(max(T3.date)), INTEGER) AS Year, + T3.date, + convert(year(T3.date), INTEGER) AS Year, FROM study.Departure T3 WHERE T3.date IS NOT NULL AND T3.qcstate.publicdata = true and cast(COALESCE(STARTDATE, '1900-01-01') as date) <= T3.date and cast(COALESCE(ENDDATE, curdate()) as date) >= cast(T3.date as date) -group by id UNION ALL diff --git a/ehr/resources/queries/study/demographicsCurLocation.query.xml b/ehr/resources/queries/study/demographicsCurLocation.query.xml index f707cc45d..e9ed2cdc0 100644 --- a/ehr/resources/queries/study/demographicsCurLocation.query.xml +++ b/ehr/resources/queries/study/demographicsCurLocation.query.xml @@ -15,7 +15,6 @@ - 40 @@ -23,7 +22,6 @@ - 40 /query/executeQuery.view?schemaName=study& diff --git a/ehr/resources/queries/study/demographicsLastHousing.query.xml b/ehr/resources/queries/study/demographicsLastHousing.query.xml index 2290cd1bf..9f37e214d 100644 --- a/ehr/resources/queries/study/demographicsLastHousing.query.xml +++ b/ehr/resources/queries/study/demographicsLastHousing.query.xml @@ -15,7 +15,6 @@ - 40 @@ -23,7 +22,6 @@ - 40 /query/executeQuery.view?schemaName=study& diff --git a/ehr/resources/queries/study/studyData.query.xml b/ehr/resources/queries/study/studyData.query.xml index e340be09e..4afd64c04 100644 --- a/ehr/resources/queries/study/studyData.query.xml +++ b/ehr/resources/queries/study/studyData.query.xml @@ -57,7 +57,6 @@ urn:ehr.labkey.org/#ParentId - 60 urn:ehr.labkey.org/#Account diff --git a/ehr/resources/reports/schemas/study/Pedigree/Pedigree.r b/ehr/resources/reports/schemas/study/Pedigree/Pedigree.r index f8a6c4f63..bda5bf9a1 100644 --- a/ehr/resources/reports/schemas/study/Pedigree/Pedigree.r +++ b/ehr/resources/reports/schemas/study/Pedigree/Pedigree.r @@ -15,8 +15,6 @@ library(Rlabkey) labkey.acceptSelfSignedCerts(); -#NOTE: to run directly in R instead of through labkey, uncomment this: -#labkey.url.base = "http://localhost:8080/labkey/" if ((length(labkey.data$id) == 0) | (is.na(labkey.data$dam) & is.na(labkey.data$sire))){ png(filename="${imgout:myscatterplot}", width = 650, height = 150); diff --git a/ehr/resources/reports/schemas/study/Pedigree/PedigreeDown.r b/ehr/resources/reports/schemas/study/Pedigree/PedigreeDown.r new file mode 100644 index 000000000..fdbe77eba --- /dev/null +++ b/ehr/resources/reports/schemas/study/Pedigree/PedigreeDown.r @@ -0,0 +1,78 @@ +## +# Copyright (c) 2010-2015 LabKey Corporation +# +# Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0 +## + +options(echo=FALSE) +library(visPedigree) +library(Rlabkey) + +labkey.setCurlOptions(ssl.verifypeer=FALSE, ssl.verifyhost=FALSE) + +id <- as.character(labkey.data$id) +method <- "down" + +#NOTE: to run from local machine uncomment these lines, it should also run in R, +#but note that you have to supply an animal id above, because it will not know labkey.data$id +#labkey.url.base = "http://localhost:8080/" +#labkey.url.path = "/WNPRC/EHR" + +print(labkey.url.base) + +# Gets the offspring given the animal id +allOffspring <- labkey.selectRows( + baseUrl=labkey.url.base, + #to run directly in R, uncomment this line. otherwise providing a containerPath is not necessary + folderPath=labkey.url.path, + schemaName="study", + queryName="demographicsOffspring", + #colSelect=c('Id', 'Dam','Sire', 'Gender', 'Status'), + showHidden = TRUE, + colFilter=makeFilter(c("Id","EQUALS",id)), + colNameOpt = 'fieldname', #rname + #showHidden = FALSE +) + +# exit early if there is no offspring for this animal +if (nrow(allOffspring)==0){ + png(filename="${imgout:myscatterplot}", width = 650, height = 150); + plot(0, 0, type='n', xaxt='n', yaxt='n', bty='n', ann=FALSE ) + title(main = "No offspring data found for selected animal.", sub = NULL, xlab = NULL, ylab = NULL, + line = NA, outer = FALSE) + +} else { + + #this section queries labkey to obtain the pedigree data + allPed <- labkey.selectRows( + baseUrl=labkey.url.base, + #to run directly in R, uncomment this line. otherwise providing a containerPath is not necessary + folderPath=labkey.url.path, + schemaName="study", + queryName="Pedigree", + colSelect=c('Id', 'Dam','Sire', 'Gender'), + showHidden = TRUE, + colNameOpt = 'fieldname', #rname + #showHidden = FALSE + ) + + #Rename gender to sex for the vispedigree program + colnames(allPed)<-c('Id', 'Dam', 'Sire', 'Sex') + + #need to replace WNPRC sex codes with strings + allPed$Sex[allPed$Sex == 1] <- "male" + allPed$Sex[allPed$Sex == 2] <- "female" + allPed$Sex[allPed$Sex == 3] <- NA + + tidy_ped <- + tidyped(ped = allPed, cand=c(id),trace=method) + + png(file="${imgout:labkey_visPedigree.png}") + visped(tidy_ped, showgraph = TRUE) + #dev.off() + + pdf(file="${pdfout:labkey_visPedigree.pdf}") + visped(tidy_ped, showgraph = TRUE) + #dev.off() + +} diff --git a/ehr/resources/reports/schemas/study/Pedigree/PedigreeDown.report.xml b/ehr/resources/reports/schemas/study/Pedigree/PedigreeDown.report.xml new file mode 100644 index 000000000..9e38d0f44 --- /dev/null +++ b/ehr/resources/reports/schemas/study/Pedigree/PedigreeDown.report.xml @@ -0,0 +1,8 @@ + + + + + query + + + \ No newline at end of file diff --git a/ehr/resources/reports/schemas/study/Pedigree/PedigreeUp.r b/ehr/resources/reports/schemas/study/Pedigree/PedigreeUp.r new file mode 100644 index 000000000..5e95ca201 --- /dev/null +++ b/ehr/resources/reports/schemas/study/Pedigree/PedigreeUp.r @@ -0,0 +1,51 @@ +## +# Copyright (c) 2010-2015 LabKey Corporation +# +# Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0 +## + +options(echo=FALSE) +library(visPedigree) +library(Rlabkey) + +labkey.setCurlOptions(ssl.verifypeer=FALSE, ssl.verifyhost=FALSE) + +id <- as.character(labkey.data$id) +method <- "up" + +#NOTE: to run from local machine uncomment these lines, it should also run in R, +#but note that you have to supply an animal id, because it will not know labkey.data$id +#labkey.url.base = "http://localhost:8080/" +#labkey.url.path = "/WNPRC/EHR" + +#this section queries labkey to obtain the pedigree data +allPed <- labkey.selectRows( + baseUrl=labkey.url.base, + #to run directly in R, uncomment this line. otherwise providing a containerPath is not necessary + folderPath=labkey.url.path, + schemaName="study", + queryName="Pedigree", + colSelect=c('Id', 'Dam','Sire', 'Gender'), + showHidden = TRUE, + colNameOpt = 'fieldname', #rname + #showHidden = FALSE +) + +#Rename gender to sex for the vispedigree program +colnames(allPed)<-c('Id', 'Dam', 'Sire', 'Sex') + +#need to replace WNPRC sex codes with strings +allPed$Sex[allPed$Sex == 1] <- "male" +allPed$Sex[allPed$Sex == 2] <- "female" +allPed$Sex[allPed$Sex == 3] <- NA + +tidy_ped <- + tidyped(ped = allPed, cand=c(id),trace=method) + +png(file="${imgout:labkey_visPedigree.png}") +visped(tidy_ped, showgraph = TRUE) +#dev.off() + +pdf(file="${pdfout:labkey_visPedigree.pdf}") +visped(tidy_ped, showgraph = TRUE) +#dev.off() diff --git a/ehr/resources/reports/schemas/study/Pedigree/PedigreeUp.report.xml b/ehr/resources/reports/schemas/study/Pedigree/PedigreeUp.report.xml new file mode 100644 index 000000000..9e38d0f44 --- /dev/null +++ b/ehr/resources/reports/schemas/study/Pedigree/PedigreeUp.report.xml @@ -0,0 +1,8 @@ + + + + + query + + + \ No newline at end of file diff --git a/ehr/resources/schemas/dbscripts/postgresql/ehr-20.000-20.001.sql b/ehr/resources/schemas/dbscripts/postgresql/ehr-20.000-20.001.sql new file mode 100644 index 000000000..c89bcda4a --- /dev/null +++ b/ehr/resources/schemas/dbscripts/postgresql/ehr-20.000-20.001.sql @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2020 LabKey Corporation + * + * Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0 + */ + +-- ehr-17.20-17.21.sql +-- contents of ehr-17.20-17.21.sql script are not in rolled up ehr-0.00-18.10.sql, since they got added and merged after the rollup. +-- So, including it below with a slight variation - add cols only if it doesn't exist since it probably exists on wnprc's postgres db +ALTER TABLE ehr.project ALTER COLUMN Title TYPE VARCHAR(400); + +CREATE FUNCTION ehr.handleAddContactToProtocol() RETURNS VOID AS $$ +DECLARE +BEGIN + IF NOT EXISTS ( + SELECT column_name + FROM information_schema.columns + WHERE table_name='protocol' and table_schema='ehr' and column_name='contacts' + ) + THEN + ALTER TABLE ehr.protocol ADD contacts VARCHAR; + END IF; +END; +$$ LANGUAGE plpgsql; + +SELECT ehr.handleAddContactToProtocol(); + +DROP FUNCTION ehr.handleAddContactToProtocol(); + +DELETE FROM ehr.qcStateMetadata WHERE QCStateLabel = 'Started'; +DELETE FROM ehr.status WHERE Label = 'Started'; + +INSERT INTO ehr.qcStateMetadata + (QCStateLabel,draftData,isDeleted,isRequest) +VALUES + ('Started', TRUE, FALSE, FALSE); +INSERT INTO ehr.status + (label,Description,PublicData,DraftData,isDeleted,isRequest,allowFutureDates) +VALUES + ('Started', 'Record has started, but not completed',TRUE,FALSE,FALSE,FALSE,FALSE); + + +-- ehr-17.21-17.22.sql +-- contents of ehr-17.21-17.22.sql script are not in rolled up ehr-0.00-18.10.sql, since they got added and merged after the rollup. +-- So, including it below with a slight variation - create table only if it doesn't exist since it probably exists on wnprc's postgres db +CREATE FUNCTION ehr.createTable_form_framework_types() RETURNS VOID AS $$ +DECLARE +BEGIN + IF NOT EXISTS(SELECT * FROM pg_tables WHERE tablename = 'form_framework_types' AND schemaname = 'ehr') + THEN + CREATE TABLE ehr.form_framework_types ( + RowId SERIAL NOT NULL, + + schemaname varchar(255) DEFAULT NULL, + queryname varchar(255) DEFAULT NULL, + framework varchar(255) DEFAULT NULL, + + Container ENTITYID NOT NULL, + CreatedBy USERID, + Created TIMESTAMP, + ModifiedBy USERID, + Modified TIMESTAMP, + + CONSTRAINT PK_form_framework_types PRIMARY KEY (schemaname, queryname) + ); + END IF; +END; +$$ LANGUAGE plpgsql; + +SELECT ehr.createTable_form_framework_types(); + +DROP FUNCTION ehr.createTable_form_framework_types(); + +CREATE FUNCTION ehr.handleAddSpeciesToSupplementalPedigree() RETURNS VOID AS $$ +DECLARE +BEGIN + IF NOT EXISTS ( + SELECT column_name + FROM information_schema.columns + WHERE table_name='supplemental_pedigree' and table_schema='ehr' and column_name='spcies' + ) + THEN + ALTER TABLE ehr.supplemental_pedigree ADD species VARCHAR(4000); + END IF; +END; +$$ LANGUAGE plpgsql; + +SELECT ehr.handleAddSpeciesToSupplementalPedigree(); + +DROP FUNCTION ehr.handleAddSpeciesToSupplementalPedigree(); diff --git a/ehr/resources/schemas/dbscripts/sqlserver/ehr-20.000-20.001.sql b/ehr/resources/schemas/dbscripts/sqlserver/ehr-20.000-20.001.sql new file mode 100644 index 000000000..1568424f3 --- /dev/null +++ b/ehr/resources/schemas/dbscripts/sqlserver/ehr-20.000-20.001.sql @@ -0,0 +1,36 @@ +--ehr-17.20-17.21.sql +-- contents of ehr-17.20-17.21.sql script are not in rolled up ehr-0.00-18.10.sql, since they got added and merged after the rollup, so including it below +ALTER TABLE ehr.protocol ADD contacts VARCHAR(200); + +-- Merging upgrade scripts, ensure that we end up with rows for 'Started' in both tables +DELETE FROM ehr.qcStateMetadata WHERE QCStateLabel = 'Started'; +DELETE FROM ehr.status WHERE Label = 'Started'; + +INSERT INTO ehr.qcStateMetadata + (QCStateLabel,draftData,isDeleted,isRequest) +VALUES + ('Started', 1, 0, 0); +INSERT INTO ehr.status + (label,Description,PublicData,DraftData,isDeleted,isRequest,allowFutureDates) +VALUES + ('Started', 'Record has started, but not completed',1,0,0,0,0); + +-- ehr-17.21-17.22.sql +-- contents of ehr-17.21-17.22.sql script are not in rolled up ehr-0.00-18.10.sql, since they got added and merged after the rollup, so including it below +CREATE TABLE ehr.form_framework_types ( + RowId INT IDENTITY(1,1) NOT NULL, + + schemaname varchar(255) DEFAULT NULL, + queryname varchar(255) DEFAULT NULL, + framework varchar(255) DEFAULT NULL, + + Container ENTITYID NOT NULL, + CreatedBy USERID, + Created datetime, + ModifiedBy USERID, + Modified datetime, + + CONSTRAINT PK_form_framework_types PRIMARY KEY (schemaname, queryname) +); + +ALTER TABLE ehr.supplemental_pedigree ADD species NVARCHAR(4000); diff --git a/ehr/resources/schemas/ehr.xml b/ehr/resources/schemas/ehr.xml index d679feb82..ea9fb7d51 100644 --- a/ehr/resources/schemas/ehr.xml +++ b/ehr/resources/schemas/ehr.xml @@ -762,6 +762,19 @@ + + + + + + + + + + + + +
Deprecated. Configuration for ExtJS 3-based data entry formsForm Panel Sections @@ -1141,6 +1154,7 @@ truetrue +
@@ -1670,6 +1684,7 @@ entityid +
@@ -1780,6 +1795,7 @@ Minimum # of Days Betweentrue + false true @@ -1804,11 +1820,6 @@ false true - - Additional Information - true - false - false true diff --git a/ehr/resources/schemas/ehr_lookups.xml b/ehr/resources/schemas/ehr_lookups.xml index 55857227d..29c7aa9c7 100644 --- a/ehr/resources/schemas/ehr_lookups.xml +++ b/ehr/resources/schemas/ehr_lookups.xml @@ -63,7 +63,6 @@ false false true - false true @@ -997,7 +996,6 @@ true - false false false @@ -1229,7 +1227,6 @@ Row Id - false diff --git a/ehr/resources/scripts/ehr/ScriptHelper.js b/ehr/resources/scripts/ehr/ScriptHelper.js index dd4d8a851..e8973cf4f 100644 --- a/ehr/resources/scripts/ehr/ScriptHelper.js +++ b/ehr/resources/scripts/ehr/ScriptHelper.js @@ -89,6 +89,7 @@ EHR.Server.ScriptHelper = function(extraContext, event, EHR){ removeTimeFromDate: false, removeTimeFromEndDate: false, allowRequestsInPast: false, + allowRequestsInDistantFuture: false, allowDeadIds: false, allowAnyId: false, skipIdFormatCheck: false, @@ -137,7 +138,7 @@ EHR.Server.ScriptHelper = function(extraContext, event, EHR){ //we allow the client to pass limited options using extraContext //this function is where all processing of client JSON -> server options should reside function setScriptOptionsFromExtraContext(){ - LABKEY.ExtAdapter.each(['skipIdFormatCheck', 'allowAnyId', 'allowDatesInDistantPast'], function(name){ + LABKEY.ExtAdapter.each(['skipIdFormatCheck', 'allowAnyId', 'allowDatesInDistantPast', 'allowRequestsInDistantFuture'], function(name){ if (extraContext[name]) scriptOptions[name] = extraContext[name]; }, this); @@ -167,6 +168,10 @@ EHR.Server.ScriptHelper = function(extraContext, event, EHR){ return scriptOptions.allowRequestsInPast; }, + shouldAllowRequestsInDistantFuture: function() { + return scriptOptions.allowRequestsInDistantFuture + }, + shouldRemoveTimeFromEndDate: function(){ return scriptOptions.removeTimeFromEndDate; }, diff --git a/ehr/resources/scripts/ehr/triggers.js b/ehr/resources/scripts/ehr/triggers.js index 8ae36dc79..700cb8512 100644 --- a/ehr/resources/scripts/ehr/triggers.js +++ b/ehr/resources/scripts/ehr/triggers.js @@ -628,15 +628,20 @@ EHR.Server.Triggers.rowInit = function(helper, scriptErrors, row, oldRow){ EHR.Server.Utils.addError(scriptErrors, 'project', 'Project must be numeric: ' + row.project, 'ERROR'); delete row.project; } + //Make sure that the project is a number validateAssignment method is expecting a integer. + var intProject; + if (row.project){ + intProject = parseInt(row.project); + } //skip if doing assignments if (!helper.isQuickValidation() && !helper.isETL() && - row.project && row.Id && row.date && - !helper.getJavaHelper().isDefaultProject(row.project) && + intProject && row.Id && row.date && + !helper.getJavaHelper().isDefaultProject(intProject) && !helper.isSkipAssignmentCheck() ){ - var assignmentErrors = helper.getJavaHelper().validateAssignment(row.Id, row.project, row.date); + var assignmentErrors = helper.getJavaHelper().validateAssignment(row.Id, intProject, row.date); if (assignmentErrors){ EHR.Server.Utils.addError(scriptErrors, 'project', assignmentErrors, helper.getErrorSeverityForImproperAssignment()); } diff --git a/ehr/resources/scripts/ehr/validation.js b/ehr/resources/scripts/ehr/validation.js index 5c16625ef..e2ecbaab6 100644 --- a/ehr/resources/scripts/ehr/validation.js +++ b/ehr/resources/scripts/ehr/validation.js @@ -26,7 +26,7 @@ EHR.Server.Validation = { */ checkRestraint: function(row, scriptErrors){ if (row.restraint && !LABKEY.ExtAdapter.isDefined(row.restraintDuration)) - EHR.Server.Utils.scriptErrors(errors, 'restraintDuration', 'Must enter time restrained', 'INFO'); + EHR.Server.Utils.addError(scriptErrors, 'restraintDuration', 'Must enter time restrained', 'INFO'); }, diff --git a/ehr/resources/views/animalHistory.html b/ehr/resources/views/animalHistory.html index a36ef1f50..c0dddfe7f 100644 --- a/ehr/resources/views/animalHistory.html +++ b/ehr/resources/views/animalHistory.html @@ -1,18 +1,17 @@ - - - - - - \ No newline at end of file + Ext4.onReady(function (){ + var webpart = <%=webpartContext%>; + var ctx = EHR.Utils.getEHRContext(webpart.wrapperDivId, ['DefaultAnimalHistoryReport']); + if(!ctx) + return; + + Ext4.create('EHR.panel.AnimalHistoryPanel', { + defaultReport: ctx.DefaultAnimalHistoryReport, + defaultTab: 'General', + showFilterOptionsTitle: true, + renderTo: webpart.wrapperDivId + }); + }); + + \ No newline at end of file diff --git a/ehr/resources/views/begin.html b/ehr/resources/views/begin.html index a1933739c..4739d1577 100644 --- a/ehr/resources/views/begin.html +++ b/ehr/resources/views/begin.html @@ -3,11 +3,6 @@ Ext4.onReady(createNavMenu); function createNavMenu(){ - if (!window.EHR || !window.EHR.ext || !window.EHR.ext.NavMenu){ - Ext4.defer(createNavMenu, 10); - return; - } - var webpart = <%=webpartContext%>; var ctx = EHR.Utils.getEHRContext(webpart.wrapperDivId); @@ -34,7 +29,7 @@ '
' ); - new EHR.ext.NavMenu({ + Ext4.create('EHR.NavMenu',{ width: 270, renderTo: 'ehrMenu1_'+webpart.wrapperDivId, sections: [ @@ -71,7 +66,7 @@ }); - new EHR.ext.NavMenu({ + Ext4.create('EHR.NavMenu', { width: 270, renderTo: 'ehrMenu2_'+webpart.wrapperDivId, sections: [ @@ -121,7 +116,7 @@ } - new EHR.ext.NavMenu(menuCfg); + Ext4.create('EHR.NavMenu', menuCfg); } diff --git a/ehr/resources/views/dataEntry.html b/ehr/resources/views/dataEntry.html index 83cab4fde..f237c6a03 100644 --- a/ehr/resources/views/dataEntry.html +++ b/ehr/resources/views/dataEntry.html @@ -202,7 +202,7 @@ if(sec5.items.length) menuCfg.sections.push(sec5); - new EHR.ext.NavMenu(menuCfg).render(); + Ext4.create('EHR.NavMenu', menuCfg); //only give a task manager if they have access to at least 1 form if(menuCfg.sections.length){ diff --git a/ehr/resources/views/manageRecord.html b/ehr/resources/views/manageRecord.html index b453858ea..e253dc841 100644 --- a/ehr/resources/views/manageRecord.html +++ b/ehr/resources/views/manageRecord.html @@ -17,7 +17,7 @@ queryName: LABKEY.ActionURL.getParameter('queryName'), keyField: LABKEY.ActionURL.getParameter('keyField'), keyValue: Ext.util.Format.htmlDecode(LABKEY.ActionURL.getParameter('key')), - title: LABKEY.ActionURL.getParameter('queryName'), + title: (LABKEY.ActionURL.getParameter('title') || EHR.Utils.toTitleCase(LABKEY.ActionURL.getParameter('queryName'))), metadata: EHR.Metadata.getTableMetadata(LABKEY.ActionURL.getParameter('queryName'), metaSources) }] }); diff --git a/ehr/resources/views/manageRequest.html b/ehr/resources/views/manageRequest.html index d3c2a1c0a..68ec48d35 100644 --- a/ehr/resources/views/manageRequest.html +++ b/ehr/resources/views/manageRequest.html @@ -1,3 +1,27 @@ + + + + - - - - - \ No newline at end of file + /* get the participant id from the request URL: this parameter is required. */ + var participantId = LABKEY.ActionURL.getParameter('participantId'); + + if (!participantId){ + alert('Must Provide Id'); + return; + } + + var title = 'Animal Details: ' + participantId; + document.title = title; + LABKEY.Utils.setWebpartTitle(title, webpart.id); + + Ext4.create('EHR.panel.ParticipantDetailsPanel', { + participantId: participantId, + defaultReport: ctx.DefaultAnimalHistoryReport, + defaultTab: 'General', + autoLoadDefaultTab: true + }).render(webpart.wrapperDivId); + }); + \ No newline at end of file diff --git a/ehr/resources/views/populateInitialData.html b/ehr/resources/views/populateInitialData.html index f2dcefffc..4583adfe6 100644 --- a/ehr/resources/views/populateInitialData.html +++ b/ehr/resources/views/populateInitialData.html @@ -429,7 +429,7 @@ ['Clinpath', 'formSections', 'ehr-abstractpanel', '1', '', '', '', '', '', '', '' ], ['Clinpath', 'formHeaders', 'ehr-gridformpanel', '2', 'Clinpath Runs', 'study', '', 'Task,Assay', '', '', '' ], ['Clinpath', 'formSections', 'ehr-gridformpanel', '3', 'Bacteriology Results', 'study', '', 'Task,Assay', '', '', 'add,copyfromclinpath,requestdelete,selectall,duplicate,bulk_edit,sortany,apply_template,save_template' ], - ['Clinpath', 'formSections', 'ehr-gridformpanel', '4', 'Chemistry Results', 'study', '', 'Task,Assay', '', '', 'add,addchemexcel,copyfromclinpath,requestdelete,selectall,duplicate,bulk_edit,sortany,apply_template,save_template' ], + ['Clinpath', 'formSections', 'ehr-gridformpanel', '4', 'Chemistry Results', 'study', '', 'Task,Assay', '', '', 'add,addchemexcel,addchemvetaxcel,copyfromclinpath,requestdelete,selectall,duplicate,bulk_edit,sortany,apply_template,save_template' ], ['Clinpath', 'formSections', 'ehr-gridformpanel', '5', 'Hematology Results', 'study', '', 'Task,Assay', '', '', 'add,addhematologyexcel,requestdelete,copyfromclinpath,selectall,duplicate,bulk_edit,sortany,apply_template,save_template' ], ['Clinpath', 'formSections', 'ehr-gridformpanel', '6', 'Hematology Morphology', 'study', '', 'Task,Assay', '', '', 'add,requestdelete,copyfromclinpath,selectall,duplicate,bulk_edit,sortany,apply_template,save_template' ], ['Clinpath', 'formSections', 'ehr-gridformpanel', '7', 'Immunology Results', 'study', '', 'Task,Assay', '', '', 'add,requestdelete,copyfromclinpath,selectall,duplicate,bulk_edit,sortany,apply_template,save_template' ], @@ -604,7 +604,7 @@ ['abstractClin' ,'Clinical' ,'js' ,'Abstract' ,'true' ,'' ,'study' ,'abstract', '', '', 'date', 'false', 'false', '', 'qcstate/publicdata', 'This report contains a summary of the animal, including demographics, assignments and weight'], ['encounters' ,'Clinical' ,'query' ,'Clinical Encounters' ,'true' ,'' ,'study' ,'Clinical Encounters', '', '', 'date', 'false', 'false', '', 'qcstate/publicdata', 'This report contains one record for each encounter with each animal, including surergies, exams, procedures, etc.'], ['clinremarks' ,'Clinical' ,'query' ,'Clinical Remarks' ,'true' ,'' ,'study' ,'Clinical Remarks', '', '', 'date', 'false', 'false', '', 'qcstate/publicdata', 'This report contains the clinical remarks entered about each animal'], - ['diarrheaCalendar' ,'Clinical' ,'query' ,'Diarrhea Calendar' ,'TRUE' ,'' ,'study' ,'diarrheaCalendar', '', '', '', 'FALSE', 'FALSE', '', 'qcstate/publicdata', 'This report shows a calendar view of the diarrhea observations entered by daily obs.'], + ['diarrheaCalendar' ,'Clinical' ,'js' ,'Diarrhea Calendar' ,'TRUE' ,'' ,'study' ,'diarrheaCalendar', '', '', '', 'FALSE', 'FALSE', '', 'qcstate/publicdata', 'This report shows a calendar view of the diarrhea observations entered by daily obs.'], ['fullHistory' ,'Clinical' ,'query' ,'Full History' ,'true' ,'' ,'study' ,'StudyData', 'Full History', '', 'date', 'false', 'false', '', 'qcstate/publicdata', 'Contains all clinical data on the animal'], ['fullHistoryPlusObs' ,'Clinical' ,'query' ,'Full History Plus Obs' ,'true' ,'' ,'study' ,'StudyData', 'Full History Plus Obs', '', 'date', 'false', 'false', '', 'qcstate/publicdata', 'Similar to Full History, but also contains irregular observations'], ['allObs' ,'Clinical' ,'js' ,'Irregular Obs' ,'true' ,'' ,'study' ,'irregularObs', '', '', 'date', 'false', 'true', '', '', 'This report displays irregular observations, including cage observations'], @@ -8072,9 +8072,9 @@ } function makeObject(config, data, fields){ - Ext.each(data, function(row){ + Ext4.each(data, function(row){ var record = {}; - Ext.each(fields, function(f, idx){ + Ext4.each(fields, function(f, idx){ record[f] = row[idx]; }, this); config.rows.push(record); diff --git a/ehr/resources/views/requestServices.html b/ehr/resources/views/requestServices.html index 1791d4847..daf367f07 100644 --- a/ehr/resources/views/requestServices.html +++ b/ehr/resources/views/requestServices.html @@ -1,100 +1,166 @@ \ No newline at end of file diff --git a/ehr/resources/web/ehr/DataEntryUtils.js b/ehr/resources/web/ehr/DataEntryUtils.js index d08f4a284..d838a6473 100644 --- a/ehr/resources/web/ehr/DataEntryUtils.js +++ b/ehr/resources/web/ehr/DataEntryUtils.js @@ -814,7 +814,7 @@ EHR.DataEntryUtils = new function(){ containerPath: ctx ? ctx['EHRStudyContainer'] : null, schemaName: 'ehr', queryName: 'project', - columns: 'project,displayName,account,name,protocol,protocol/displayName,title,investigatorId/lastName', + columns: 'project,displayName,account,name,protocol,protocol/displayName,protocol/inves,protocol/investigatorId,title,investigatorId/lastName', //filterArray: [LABKEY.Filter.create('enddate', null, LABKEY.Filter.Types.ISBLANK)], sort: 'displayName', storeId: storeId, @@ -989,23 +989,27 @@ EHR.DataEntryUtils = new function(){ } } else { - Ext4.Object.each(permMap, function(schemaName, queries) { - // minor improvement. non-study tables cannot have per-table permissions, so instead we check - // for the container-level DataEntryPermission + // Check each query to test in turn + Ext4.each(queriesToTest, function(queryObject) { + var schemaPermissions = permMap[queryObject.schemaName]; + var queryPermissions = (schemaPermissions || {})[queryObject.queryName]; + var permissionToTest = permissionName; - if (schemaName.toLowerCase() != 'study'){ + if (queryObject.schemaName.toLowerCase() != 'study'){ + // minor improvement. non-study tables cannot have per-table permissions, so instead we check + // for the container-level DataEntryPermission permissionToTest = 'org.labkey.api.ehr.security.EHRDataEntryPermission'; } - Ext4.Object.each(queries, function(queryName, permissions) { - if (!permissions[permissionToTest]){ - hasPermission = false; - return false; - } - }, this); - if (!hasPermission) - return false; - }, this); + if (Ext4.isDefined(schemaPermissions) && Ext4.isDefined(queryPermissions) && permissionToTest in queryPermissions) { + // Do nothing: we have security + } + else { + // If we don't have any permissions defined for the query, assume we don't have access + hasPermission = false; + return false; // short circuit out of jQuery.each + } + }); } return hasPermission; diff --git a/ehr/resources/web/ehr/ExtOverrides.js b/ehr/resources/web/ehr/ExtOverrides.js deleted file mode 100644 index 172cd5efe..000000000 --- a/ehr/resources/web/ehr/ExtOverrides.js +++ /dev/null @@ -1,227 +0,0 @@ -/* - * Copyright (c) 2012-2019 LabKey Corporation - * - * Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0 - */ - -//This will contain ext overrides -Ext.namespace('EHR.ext'); - - - -//a css fix for Ext datepicker and tabpanel -Ext.menu.DateMenu.prototype.addClass('extContainer'); -Ext.TabPanel.prototype.addClass('extContainer'); - - -//NOTE: In IE8, DatePickers render half-width -//see: http://www.opendebug.com/article/533989 -Ext.override (Ext.menu.Menu, { - autoWidth:function (){ - //only append if input is numeric - if (!isNaN(this.width)) - this.width += 'px'; - } -}); - -//Ext's 3.1 documentation says this should be the code. -// the purpose is to allow null values in numeric fields -Ext.data.Types.INT.convert = function(v){ - return v !== undefined && v !== null && v !== '' ? - parseInt(String(v).replace(Ext.data.Types.stripRe, ''), 10) : (this.useNull ? null : 0); -}; - -Ext.data.Types.FLOAT.convert = function(v){ - return v !== undefined && v !== null && v !== '' ? - parseFloat(String(v).replace(Ext.data.Types.stripRe, ''), 10) : (this.useNull ? null : 0); -}; - -//if not set to true, null numeric values will automatically be coerced to zero -Ext.data.Field.prototype.useNull = true; - - - -Ext.override(Ext.form.CheckboxGroup, { - getValueAsString: function(delim) - { - delim = delim || ','; - var vals = []; - Ext.each(this.getValue(), function(c){ - vals.push(c.inputValue); - }, this); - return vals.join(delim); - }, - removeAll: function () { - this.panel.items.each(function(col){ - col.items.each(function(item){ - this.items.remove(item); - col.remove(item); - }) - }, this); - } -}); - -//Ext is going to coerse the value into a string, which doesnt work well when it's null -Ext.override(Ext.form.RadioGroup, { - setValueForItem: function(val){ - val = (val===null ? '' : val); - Ext.form.RadioGroup.superclass.setValueForItem.call(this, val); - }, - blankText: 'This field is required' -}); - - -Ext.override(Ext.Panel, { - setReadOnly: function(val){ - if(!this.items){ - console.log(this) - } - else { - this.items.each(function(item){ - if(item.setReadOnly){ - item.setReadOnly(val); - } - else { - item.setDisabled(val) - } - }, this); - } - } -}); - -//overridden b/c if you try to set the value of a combo prior to store loading, it will display -//the raw value, not display value -Ext.override(LABKEY.ext.ComboBox, { - setValue: function(v){ - if(this.store && !this.store.fields){ - this.initialValue = v; - } - - LABKEY.ext.ComboBox.superclass.setValue.call(this, v); - } -}); - - -//overridden b/c readOnly has no effect on checkboxes and radios. this will disable the element, making it truly read only -Ext.override(Ext.form.Checkbox, { - setReadOnly: function(val){ - Ext.form.Checkbox.superclass.setReadOnly.apply(this, arguments); - this.setDisabled(val); - } -}); - -Ext.override(Ext.data.Store, { - add : function(records) { - var i, len, record, index; - - records = [].concat(records); - if (records.length < 1) { - return; - } - - for (i = 0, len = records.length; i < len; i++) { - record = records[i]; - - record.join(this); - - if (record.dirty || record.phantom) { - this.modified.push(record); - } - } - - index = this.data.length; - this.data.addAll(records); - - if (this.snapshot) { - this.snapshot.addAll(records); - } - - this.fireEvent('add', this, records, index); - }, - insert : function(index, records) { - var i, len, record; - - records = [].concat(records); - for (i = 0, len = records.length; i < len; i++) { - record = records[i]; - - this.data.insert(index + i, record); - record.join(this); - - if (record.dirty || record.phantom) { - this.modified.push(record); - } - } - - if (this.snapshot) { - this.snapshot.addAll(records); - } - - this.fireEvent('add', this, records, index); - } -}); - -//only overridden to remove setting a default nullCaption. this is moved to the combo tpl -Ext.override(LABKEY.ext.Store, { - onLoad : function(store, records, options) { - this.isLoading = false; - - //remember the name of the id column - this.idName = this.reader.meta.id; - - if(this.nullRecord) - { - //create an extra record with a blank id column - //and the null caption in the display column - var data = {}; - data[this.reader.meta.id] = ""; - data[this.nullRecord.displayColumn] = this.nullRecord.nullCaption || this.nullCaption || null; - - var recordConstructor = Ext.data.Record.create(this.reader.meta.fields); - var record = new recordConstructor(data, -1); - this.insert(0, record); - } - } -}); - -//Ext.override(Ext.form.ComboBox, { -// doQuery : function(q, forceAll){ -// q = Ext.isEmpty(q) ? '' : q; -// var qe = { -// query: q, -// forceAll: forceAll, -// combo: this, -// cancel:false -// }; -// if(this.fireEvent('beforequery', qe)===false || qe.cancel){ -// return false; -// } -// q = qe.query; -// forceAll = qe.forceAll; -// if(forceAll === true || (q.length >= this.minChars)){ -// if(this.lastQuery !== q){ -// this.lastQuery = q; -// if(this.mode == 'local'){ -// this.selectedIndex = -1; -// if(forceAll){ -// this.store.clearFilter(); -// }else{ -// this.store.filter(this.displayField, q, this.allowAnyValue, this.caseSensitive); -// } -// this.onLoad(); -// }else{ -// this.store.baseParams[this.queryParam] = q; -// this.store.load({ -// params: this.getParams(q) -// }); -// this.expand(); -// } -// }else{ -// this.selectedIndex = -1; -// this.onLoad(); -// } -// } -// } -//}); - - diff --git a/ehr/resources/web/ehr/HousingAndAssignmentHistory.css b/ehr/resources/web/ehr/HousingAndAssignmentHistory.css deleted file mode 100644 index 233febbee..000000000 --- a/ehr/resources/web/ehr/HousingAndAssignmentHistory.css +++ /dev/null @@ -1,34 +0,0 @@ -.x-axis path, .x-axis line { - fill: none; - stroke: black; - shape-rendering: crispEdges; -} - -.x-axis text { - font-family: sans-serif; - font-size: 11px; -} - -.animal-assignments-tooltip, .animal-housing-tooltip { - position: absolute; - text-align: center; - font: 12px sans-serif; - background: lightsteelblue; - border: 2px solid black; - border-radius: 8px; -} -.animal-assignments-tooltip p, .animal-housing-tooltip p { - margin-bottom: 0; - margin-top: 0; - color: #333; - padding: 2px; -} - -.animal-assignments-tooltip .animal-housingassignments-tooltip-header, .animal-housing-tooltip .animal-housingassignments-tooltip-header { - background: #0094ff; - color: white; - padding: 6px; - border-top-left-radius: 8px; - border-top-right-radius: 8px; - font-weight: bold; -} \ No newline at end of file diff --git a/ehr/resources/web/ehr/HousingAndAssignmentHistoryTemplate.html b/ehr/resources/web/ehr/HousingAndAssignmentHistoryTemplate.html deleted file mode 100644 index fbda61a96..000000000 --- a/ehr/resources/web/ehr/HousingAndAssignmentHistoryTemplate.html +++ /dev/null @@ -1,61 +0,0 @@ - -

- -

-
- - - - - - - - - - - - - - - - - - - -
-

-

-

-

-

-
- -

- - -

(no reason specified)

- -
- -
-

- - - - - (Unrecognized assignment code: "") - -

-

-

-
-

Project:

-

-
- diff --git a/ehr/resources/web/ehr/d3-graph-template.html b/ehr/resources/web/ehr/d3-graph-template.html deleted file mode 100644 index 1680719f7..000000000 --- a/ehr/resources/web/ehr/d3-graph-template.html +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/ehr/resources/web/ehr/data/AssignmentClientStore.js b/ehr/resources/web/ehr/data/AssignmentClientStore.js index fda4e9656..0720ad6a8 100644 --- a/ehr/resources/web/ehr/data/AssignmentClientStore.js +++ b/ehr/resources/web/ehr/data/AssignmentClientStore.js @@ -21,7 +21,7 @@ Ext4.define('EHR.data.AssignmentClientStore', { if (!id || !date || !project) continue; - date = date.format(LABKEY.extDefaultDateFormat); + date = Ext4.Date.format(date, LABKEY.extDefaultDateFormat); rows.push({ Id: id, diff --git a/ehr/resources/web/ehr/data/BloodDrawClientStore.js b/ehr/resources/web/ehr/data/BloodDrawClientStore.js index 39c55dcea..d3d2004ee 100644 --- a/ehr/resources/web/ehr/data/BloodDrawClientStore.js +++ b/ehr/resources/web/ehr/data/BloodDrawClientStore.js @@ -35,7 +35,7 @@ Ext4.define('EHR.data.BloodDrawClientStore', { if (!id || !date) continue; - date = date.format(LABKEY.extDefaultDateFormat); + date = Ext4.util.Format.date(date, LABKEY.extDefaultDateFormat); if (!bloodDrawMap[id]) bloodDrawMap[id] = []; diff --git a/ehr/resources/web/ehr/data/DataEntryServerStore.js b/ehr/resources/web/ehr/data/DataEntryServerStore.js index e480d093a..dd94b72c4 100644 --- a/ehr/resources/web/ehr/data/DataEntryServerStore.js +++ b/ehr/resources/web/ehr/data/DataEntryServerStore.js @@ -264,9 +264,9 @@ Ext4.define('EHR.data.DataEntryServerStore', { // The code below is being left in place for now, but may be unnecessary. if there are no changes to apply, no edits should be made this.callParent(arguments); - if (command.command != 'delete'){ + if (!command || command.command != 'delete'){ var idProp = this.proxy.reader.getIdProperty(); - Ext4.Array.forEach(command.rows, function(row){ + Ext4.Array.forEach(records, function(row){ var record; if (row.oldKeys){ //NOTE: always test both upper and lower case. somewhat ugly, but the DB can alter the case of GUIDs diff --git a/ehr/resources/web/ehr/data/RequestStoreCollection.js b/ehr/resources/web/ehr/data/RequestStoreCollection.js index 74bbc1035..642698abd 100644 --- a/ehr/resources/web/ehr/data/RequestStoreCollection.js +++ b/ehr/resources/web/ehr/data/RequestStoreCollection.js @@ -20,5 +20,43 @@ Ext4.define('EHR.data.RequestStoreCollection', { } return this.callParent([model]); + }, + + commitChanges: function(){ + // ensure all records are using this requestid and alert if not + var requestid = this.getRequestId(); + if (requestid){ + this.clientStores.each(function(cs){ + if (cs.getFields().get('requestid') != null){ + cs.each(function(r){ + if (requestid != r.get('requestid')){ + LDK.Assert.assertEquality('Incorrect requestid for client store:' + cs.storeId, requestid, r.get('requestid')); + r.beginEdit(); + r.set('requestid', this.getRequestId()); + r.endEdit(true); + } + }, this); + } + }, this); + + this.serverStores.each(function(cs){ + if (cs.getFields().get('requestid') != null){ + cs.each(function(r){ + if (r.isRemovedRequest){ + return; //do not check these records. they have deliberately been separated. + } + + if (requestid != r.get('requestid')){ + LDK.Assert.assertEquality('Incorrect requestid for server store:' + cs.storeId, requestid, r.get('requestid')); + r.beginEdit(); + r.set('requestid', this.getRequestId()); + r.endEdit(true); + } + }, this); + } + }, this); + } + + return this.callParent(arguments); } }); \ No newline at end of file diff --git a/ehr/resources/web/ehr/data/StoreCollection.js b/ehr/resources/web/ehr/data/StoreCollection.js index ac87f8290..dbf0697d9 100644 --- a/ehr/resources/web/ehr/data/StoreCollection.js +++ b/ehr/resources/web/ehr/data/StoreCollection.js @@ -386,7 +386,8 @@ Ext4.define('EHR.data.StoreCollection', { storeId: this.collectionId + '-' + section.name, model: modelName, sectionCfg: section, - loaded: false + loaded: false, + slaveFieldsToInclude: section.slaveFieldsToInclude }); this.addClientStore(store); diff --git a/ehr/resources/web/ehr/data/WeightClientStore.js b/ehr/resources/web/ehr/data/WeightClientStore.js index 2abd61190..8f5c1c59a 100644 --- a/ehr/resources/web/ehr/data/WeightClientStore.js +++ b/ehr/resources/web/ehr/data/WeightClientStore.js @@ -23,7 +23,7 @@ Ext4.define('EHR.data.WeightClientStore', { if (!id || !date) continue; - date = date.format(LABKEY.extDefaultDateFormat); + date = Ext4.Date.format(date, LABKEY.extDefaultDateFormat); if (!weightMap[id]) weightMap[id] = []; diff --git a/ehr/resources/web/ehr/ehr_api.lib.xml b/ehr/resources/web/ehr/ehr_api.lib.xml index dad53ba1a..744ea18fd 100644 --- a/ehr/resources/web/ehr/ehr_api.lib.xml +++ b/ehr/resources/web/ehr/ehr_api.lib.xml @@ -5,6 +5,8 @@