diff --git a/ehr/resources/web/ehr/panel/GeneticCalculationSettingsPanel.js b/ehr/resources/web/ehr/panel/GeneticCalculationSettingsPanel.js index b5e217dd1..2a527591b 100644 --- a/ehr/resources/web/ehr/panel/GeneticCalculationSettingsPanel.js +++ b/ehr/resources/web/ehr/panel/GeneticCalculationSettingsPanel.js @@ -7,6 +7,7 @@ Ext4.define('EHR.panel.GeneticCalculationSettingsPanel', { extend: 'Ext.panel.Panel', initComponent: function(){ + Ext4.QuickTips.init(); Ext4.apply(this, { //width: 550, border: false, @@ -48,6 +49,15 @@ Ext4.define('EHR.panel.GeneticCalculationSettingsPanel', { fieldLabel: 'Hour Of Day', itemId: 'hourOfDay', width: 400 + },{ + xtype: 'numberfield', + hideTrigger: true, + fieldLabel: 'Day of Week', + itemId: 'dayOfWeek', + helpPopup: 'If provided, the job will run on the specified day of the week (1-7 or Sun-Sat). If blank, it will run each day.', + width: 400, + minValue: 1, + maxValue: 7 },{ xtype: 'textfield', fieldLabel: 'Container Path', @@ -96,6 +106,7 @@ Ext4.define('EHR.panel.GeneticCalculationSettingsPanel', { this.down('#isScheduled').setValue(results.isScheduled); this.down('#enabled').setValue(results.enabled); this.down('#hourOfDay').setValue(results.hourOfDay); + this.down('#dayOfWeek').setValue(results.dayOfWeek); this.down('#containerPath').setValue(results.containerPath); this.down('#kinshipValidation').setValue(results.kinshipValidation); this.down('#allowImportDuringBusinessHours').setValue(results.allowImportDuringBusinessHours) @@ -109,6 +120,7 @@ Ext4.define('EHR.panel.GeneticCalculationSettingsPanel', { containerPath: this.down('#containerPath').getValue(), enabled: this.down('#enabled').getValue(), hourOfDay: this.down('#hourOfDay').getValue(), + dayOfWeek: this.down('#dayOfWeek').getValue(), kinshipValidation: this.down('#kinshipValidation').getValue(), allowImportDuringBusinessHours: this.down('#allowImportDuringBusinessHours').getValue() }, diff --git a/ehr/src/org/labkey/ehr/EHRController.java b/ehr/src/org/labkey/ehr/EHRController.java index b41bbfe6d..b1b2cec9c 100644 --- a/ehr/src/org/labkey/ehr/EHRController.java +++ b/ehr/src/org/labkey/ehr/EHRController.java @@ -640,7 +640,7 @@ public ApiResponse execute(ScheduleGeneticCalculationForm form, BindException er errors.reject(ERROR_MSG, "Unable to find container for path: " + form.getContainerPath()); return null; } - GeneticCalculationsJob.setProperties(form.isEnabled(), c, form.getHourOfDay(), form.isKinshipValidation(), form.isAllowImportDuringBusinessHours()); + GeneticCalculationsJob.setProperties(form.isEnabled(), c, form.getHourOfDay(), form.getDayOfWeek(), form.isKinshipValidation(), form.isAllowImportDuringBusinessHours()); return new ApiSimpleResponse("success", true); } @@ -758,6 +758,7 @@ public static class ScheduleGeneticCalculationForm private boolean _enabled; private String containerPath; private int hourOfDay; + private Integer dayOfWeek; private boolean _kinshipValidation; private boolean _allowImportDuringBusinessHours; @@ -792,6 +793,16 @@ public void setHourOfDay(int hourOfDay) this.hourOfDay = hourOfDay; } + public Integer getDayOfWeek() + { + return dayOfWeek; + } + + public void setDayOfWeek(Integer dayOfWeek) + { + this.dayOfWeek = dayOfWeek; + } + public boolean isKinshipValidation() { return _kinshipValidation; @@ -828,6 +839,7 @@ public ApiResponse execute(ScheduleGeneticCalculationForm form, BindException er ret.put("isScheduled", GeneticCalculationsJob.isScheduled()); ret.put("enabled", GeneticCalculationsJob.isEnabled()); ret.put("hourOfDay", GeneticCalculationsJob.getHourOfDay()); + ret.put("dayOfWeek", GeneticCalculationsJob.getDayOfWeek()); ret.put("kinshipValidation", GeneticCalculationsJob.isKinshipValidation()); ret.put("allowImportDuringBusinessHours", GeneticCalculationsJob.isAllowImportDuringBusinessHours()); diff --git a/ehr/src/org/labkey/ehr/pipeline/GeneticCalculationsImportTask.java b/ehr/src/org/labkey/ehr/pipeline/GeneticCalculationsImportTask.java index 076698360..44def5c03 100644 --- a/ehr/src/org/labkey/ehr/pipeline/GeneticCalculationsImportTask.java +++ b/ehr/src/org/labkey/ehr/pipeline/GeneticCalculationsImportTask.java @@ -307,6 +307,14 @@ else if (kinshipTable.getSqlDialect().isPostgreSQL()) if (lineNum % 250000 == 0) { log.info("imported " + String.format("%,d", lineNum) + " rows"); + if (job != null) + { + job.updateStatusForTask(); + if (job.isCancelled()) + { + throw new CancelledException(); + } + } } } diff --git a/ehr/src/org/labkey/ehr/pipeline/GeneticCalculationsJob.java b/ehr/src/org/labkey/ehr/pipeline/GeneticCalculationsJob.java index be2b92634..db3b9b1d6 100644 --- a/ehr/src/org/labkey/ehr/pipeline/GeneticCalculationsJob.java +++ b/ehr/src/org/labkey/ehr/pipeline/GeneticCalculationsJob.java @@ -97,13 +97,15 @@ public static void schedule() if (hour == null) hour = 2; + Integer day = getDayOfWeek(); + JobDetail job = JobBuilder.newJob(GeneticCalculationsJob.class) .withIdentity(GeneticCalculationsJob.class.getCanonicalName()) .build(); Trigger trigger = TriggerBuilder.newTrigger() .withIdentity(GeneticCalculationsJob.class.getCanonicalName()) - .withSchedule(CronScheduleBuilder.dailyAtHourAndMinute(hour, 0)) + .withSchedule(day == null ? CronScheduleBuilder.dailyAtHourAndMinute(hour, 0) : CronScheduleBuilder.weeklyOnDayAndHourAndMinute(day, hour, 0)) .forJob(job) .build(); @@ -174,12 +176,23 @@ public static Integer getHourOfDay() return null; } - public static void setProperties(Boolean isEnabled, Container c, Integer hourOfDay, Boolean isKinshipValidation, Boolean allowImportDuringBusinessHours) + public static Integer getDayOfWeek() + { + Map saved = PropertyManager.getProperties(GENETICCALCULATIONS_PROPERTY_DOMAIN); + + if (saved.containsKey("dayOfWeek") & saved.get("dayOfWeek") != null) + return Integer.parseInt(saved.get("dayOfWeek")); + + return null; + } + + public static void setProperties(Boolean isEnabled, Container c, Integer hourOfDay, @Nullable Integer dayOfWeek, Boolean isKinshipValidation, Boolean allowImportDuringBusinessHours) { WritablePropertyMap props = PropertyManager.getWritableProperties(GENETICCALCULATIONS_PROPERTY_DOMAIN, true); props.put("enabled", isEnabled.toString()); props.put("container", c.getId()); props.put("hourOfDay", hourOfDay.toString()); + props.put("dayOfWeek", dayOfWeek == null ? null : dayOfWeek.toString()); props.put("kinshipValidation", isKinshipValidation.toString()); props.put("allowImportDuringBusinessHours", allowImportDuringBusinessHours.toString()); props.save(); diff --git a/ehr/test/src/org/labkey/test/util/ehr/EHRTestHelper.java b/ehr/test/src/org/labkey/test/util/ehr/EHRTestHelper.java index 3f10bd6af..fcf79a5ef 100644 --- a/ehr/test/src/org/labkey/test/util/ehr/EHRTestHelper.java +++ b/ehr/test/src/org/labkey/test/util/ehr/EHRTestHelper.java @@ -230,6 +230,16 @@ public void applyTemplate(Ext4GridRef grid, String templateName, boolean bulkEdi } } + public Locator.XPathLocator openBulkEdit(Ext4GridRef grid) + { + grid.clickTbarButton("More Actions"); + _test.waitAndClick(Locator.tag("span").withText("Bulk Edit")); + Locator.XPathLocator bulkEditWindow = Ext4Helper.Locators.window("Bulk Edit"); + _test.waitForElement(bulkEditWindow); + return bulkEditWindow; + } + + // This method toggles the bulk edit field that CONTAINS the label parameter in the label public void toggleBulkEditField(String label) { Locator.XPathLocator l = Ext4Helper.Locators.window("Bulk Edit").append(Locator.tagContainingText("label", label + ":").withClass("x4-form-item-label")); @@ -237,6 +247,14 @@ public void toggleBulkEditField(String label) _test.waitForElement(l.enabled()); } + // This method toggles the bulk edit field that has the exact label + public void toggleBulkEditExactField(String label) + { + Locator.XPathLocator l = Ext4Helper.Locators.window("Bulk Edit").append(Locator.tagWithText("label", label + ":").withClass("x4-form-item-label")); + _test.shortWait().until(ExpectedConditions.numberOfElementsToBe(l, 1)).get(0).click(); + _test.waitForElement(l.enabled()); + } + public void discardForm() { WebElement dataEntryButton = getDataEntryButton("More Actions").findElement(_test.getDriver()); diff --git a/snd/test/src/org/labkey/test/tests/snd/SNDTest.java b/snd/test/src/org/labkey/test/tests/snd/SNDTest.java index 06e6a1236..3c9f2af30 100644 --- a/snd/test/src/org/labkey/test/tests/snd/SNDTest.java +++ b/snd/test/src/org/labkey/test/tests/snd/SNDTest.java @@ -1048,6 +1048,7 @@ public void preTest() //TODO: once exp tables are exposed - do a full cleanup from snd.pkgs, exp.DomainDescriptor, exp.PropertyDomain, exp.PropertyDescriptor } + @SuppressWarnings("JUnit3StyleTestMethodInJUnit4Class") // Explicitly called as part of setup public void testSNDImport() { //go to SND Project @@ -1264,7 +1265,7 @@ public void cloneDraftPackage() assertEquals("2", fairReviewRow.getMin()); assertEquals("7", fairReviewRow.getMax()); assertEquals(":-)", fairReviewRow.getDefault()); - assertEquals(true, fairReviewRow.getRequired()); + assertTrue(fairReviewRow.getRequired()); assertEquals("lol", fairReviewRow.getRedactedText()); } @@ -1404,7 +1405,7 @@ public void saveNewPackage() assertEquals("2", reviewRow.getMin()); assertEquals("7", reviewRow.getMax()); assertEquals("men", reviewRow.getDefault()); - assertEquals(true, reviewRow.getRequired()); + assertTrue(reviewRow.getRequired()); assertEquals("and women", reviewRow.getRedactedText()); } @@ -1545,11 +1546,11 @@ public void editCategories() throws Exception CategoryEditRow surgeryRowCat = catPage.getCategory("Surgery"); assertNotNull("Surgery category should exist", surgeryRowCat); - assertEquals("Surgery category should be active", true, surgeryRowCat.getIsActive()); + assertTrue("Surgery category should be active", surgeryRowCat.getIsActive()); CategoryEditRow ourCat = catPage.getCategory(editedCategory); assertNotNull("test edit category should exist", ourCat); - assertEquals("test edit category should be inactive", false, ourCat.getIsActive()); + assertFalse("test edit category should be inactive", ourCat.getIsActive()); assertEquals("test edit category should have new description", editedCategory, ourCat.getDescription()); SelectRowsCommand catsCmd = new SelectRowsCommand("snd", "PkgCategories"); @@ -1803,12 +1804,17 @@ public void testProjectApis() throws Exception public void testSuperPackageApis() throws Exception { goToProjectHome(); - goToSchemaBrowser(); //insert super package runScript(SAVEPACKAGEAPI_CHILDREN); goToSchemaBrowser(); - viewQueryData("snd", "SuperPkgs"); + selectQuery("snd", "SuperPkgs"); + // Issue 52277: Be sure that the call to analyze the query has completed by waiting for the dependency report, + // ensuring it won't be chosen as a deadlock victim + waitForText("Dependency Report"); + Locator viewData = Locator.linkWithText("view data"); + waitAndClickAndWait(viewData); + assertTextPresent(TEST_SUPER_PKG_DESCRIPTION_1, 1); checkResults(TEST_SUPER_PKG_DESCRIPTION_1, Arrays.asList(TEST_SUPER_PKG_START_ID1 + 1, // top-level super package is the + 0, so start at + 1 @@ -1818,8 +1824,7 @@ public void testSuperPackageApis() throws Exception //update super package without cloning, with children runScript(UPDATESUPERPACKAGEAPI_CHILDREN); - goToSchemaBrowser(); - viewQueryData("snd", "SuperPkgs"); + refresh(); assertTextPresent(TEST_SUPER_PKG_DESCRIPTION_2, 1); assertTextNotPresent(TEST_SUPER_PKG_DESCRIPTION_1); checkResults(TEST_SUPER_PKG_DESCRIPTION_2, @@ -1832,8 +1837,7 @@ public void testSuperPackageApis() throws Exception //update super package with cloning runScript(UPDATESUPERPACKAGEAPI_CLONE); - goToSchemaBrowser(); - viewQueryData("snd", "SuperPkgs"); + refresh(); assertTextPresent(TEST_SUPER_PKG_DESCRIPTION_3, 1); assertTextPresent(TEST_SUPER_PKG_DESCRIPTION_2, 1); checkResults(TEST_SUPER_PKG_DESCRIPTION_3, @@ -1846,8 +1850,7 @@ public void testSuperPackageApis() throws Exception //update super package without cloning, without children runScript(UPDATESUPERPACKAGEAPI_NOCHILDREN); - goToSchemaBrowser(); - viewQueryData("snd", "SuperPkgs"); + refresh(); assertTextPresent(TEST_SUPER_PKG_DESCRIPTION_4, 1); assertTextPresent(TEST_SUPER_PKG_DESCRIPTION_3, 1); assertTextNotPresent(TEST_SUPER_PKG_DESCRIPTION_2); @@ -2170,7 +2173,7 @@ public void categoryPermissionsUI() throws Exception { value = getPermissionTableValue(i, 1); assertNotNull(value); - assertTrue(value.equals("None")); + assertEquals("None", value); categoryRows.add(i); } } @@ -2182,7 +2185,7 @@ public void categoryPermissionsUI() throws Exception { value = getPermissionTableValue(r, 1); assertNotNull(value); - assertTrue(value.equals("SND Reader")); + assertEquals("SND Reader", value); } findButton("Clear All").click(); @@ -2192,7 +2195,7 @@ public void categoryPermissionsUI() throws Exception { value = getPermissionTableValue(categoryRows.get(k), 1); assertNotNull(value); - assertTrue(value.equals("None")); + assertEquals("None", value); click(getSimpleTableCell(Locator.id("category-security"), categoryRows.get(k), 1)); clickRoleInOpenDropDown(permissions.get(k)); } @@ -2203,7 +2206,7 @@ public void categoryPermissionsUI() throws Exception { value = getPermissionTableValue(categoryRows.get(j), 1); assertNotNull(value); - assertTrue(value.equals(permissions.get(j))); + assertEquals(value, permissions.get(j)); } }