diff --git a/OConnor/resources/views/BloodCalendar.html b/OConnor/resources/views/BloodCalendar.html index 2903e7e2..3d517e25 100644 --- a/OConnor/resources/views/BloodCalendar.html +++ b/OConnor/resources/views/BloodCalendar.html @@ -177,8 +177,13 @@ var content = document.createElement('span'); - content.innerHTML = '' + animal[i][t] + week[i][t] + ''; - + // Build the anchor with DOM APIs so list-derived values (URL, animal id, entry text) can't inject markup + var anchor = document.createElement('a'); + anchor.setAttribute('style', 'font-size:14px'); + anchor.setAttribute('href', urls[i][t]); + anchor.appendChild(document.createTextNode(animal[i][t] + week[i][t])); + content.appendChild(anchor); + row.appendChild(content); column.appendChild(row); diff --git a/OConnorExperiments/src/org/labkey/oconnorexperiments/OConnorExperimentsController.java b/OConnorExperiments/src/org/labkey/oconnorexperiments/OConnorExperimentsController.java index fd6b148c..4361fe3f 100644 --- a/OConnorExperiments/src/org/labkey/oconnorexperiments/OConnorExperimentsController.java +++ b/OConnorExperiments/src/org/labkey/oconnorexperiments/OConnorExperimentsController.java @@ -16,49 +16,29 @@ package org.labkey.oconnorexperiments; -import org.apache.commons.io.FileUtils; -import org.apache.logging.log4j.LogManager; import org.labkey.api.action.ApiResponse; import org.labkey.api.action.ApiSimpleResponse; import org.labkey.api.action.FormHandlerAction; -import org.labkey.api.action.FormViewAction; import org.labkey.api.action.ReadOnlyApiAction; -import org.labkey.api.action.ReturnUrlForm; import org.labkey.api.action.SimpleViewAction; import org.labkey.api.action.SpringActionController; import org.labkey.api.collections.CaseInsensitiveHashMap; import org.labkey.api.data.Container; import org.labkey.api.data.ContainerManager; -import org.labkey.api.data.CoreSchema; -import org.labkey.api.data.DbSequenceManager; -import org.labkey.api.data.SimpleFilter; -import org.labkey.api.data.Sort; -import org.labkey.api.data.SqlExecutor; import org.labkey.api.data.TableInfo; -import org.labkey.api.data.TableSelector; -import org.labkey.api.files.FileContentService; import org.labkey.api.portal.ProjectUrls; import org.labkey.api.query.BatchValidationException; -import org.labkey.api.query.FieldKey; -import org.labkey.api.query.QueryAction; import org.labkey.api.query.QueryService; import org.labkey.api.query.QueryUpdateService; import org.labkey.api.query.UserSchema; import org.labkey.api.security.RequiresLogin; import org.labkey.api.security.RequiresPermission; -import org.labkey.api.security.User; -import org.labkey.api.security.UserManager; -import org.labkey.api.security.permissions.AdminPermission; import org.labkey.api.security.permissions.InsertPermission; import org.labkey.api.security.permissions.ReadPermission; import org.labkey.api.security.permissions.UpdatePermission; -import org.labkey.api.services.ServiceRegistry; import org.labkey.api.util.DOM; -import org.labkey.api.util.FileStream; import org.labkey.api.util.PageFlowUtil; -import org.labkey.api.util.Path; import org.labkey.api.util.URLHelper; -import org.labkey.api.view.ActionURL; import org.labkey.api.view.HtmlView; import org.labkey.api.view.HttpView; import org.labkey.api.view.JspView; @@ -66,18 +46,12 @@ import org.labkey.api.view.NotFoundException; import org.labkey.api.view.RedirectException; import org.labkey.api.view.VBox; -import org.labkey.api.webdav.WebdavResolver; -import org.labkey.api.webdav.WebdavResource; import org.labkey.oconnorexperiments.query.OConnorExperimentsUserSchema; import org.springframework.validation.BindException; import org.springframework.validation.Errors; import org.springframework.web.servlet.ModelAndView; -import java.io.File; -import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; -import java.util.Date; import java.util.List; import java.util.Map; @@ -106,316 +80,6 @@ public void addNavTrail(NavTree root) } } - @RequiresPermission(AdminPermission.class) - public static class MigrateDataAction extends FormViewAction - { - @Override - public void validateCommand(UserForm target, Errors errors) - { - } - - @Override - public ModelAndView getView(UserForm form, boolean reshow, BindException errors) - { - return new JspView<>("/org/labkey/oconnorexperiments/view/migrateData.jsp"); - } - - @Override - public boolean handlePost(UserForm form, BindException errors) throws Exception - { - if (form.isFinalMigration()) - { - LogManager.getLogger(OConnorExperimentsController.class).info("Final migration to be performed - file move events will be performed (irreversible)."); - } - - // global containers - Container sourceContainer = ContainerManager.getForPath(form.getSourceProject()); - Container targetContainer = getContainer(); - FileContentService fileContentService = FileContentService.get(); - - // get table info for the source table - UserSchema sourceSchema = QueryService.get().getUserSchema(getUser(), sourceContainer, "oconnor"); - TableInfo sourceTable = sourceSchema.getTable("simple_experiment"); - TableSelector tableSelector = new TableSelector(sourceTable, null, new Sort("ExpNumber")); - Collection> sourceCollection = tableSelector.getMapCollection(); - - // get table info for target table - UserSchema targetSchema = QueryService.get().getUserSchema(getUser(), getContainer(), OConnorExperimentsSchema.NAME); - TableInfo targetTable = targetSchema.getTable(OConnorExperimentsSchema.EXPERIMENTS); - QueryUpdateService queryUpdateService = targetTable.getUpdateService(); - BatchValidationException batchErrors = new BatchValidationException(); - - TableInfo typeTable = targetSchema.getTable(OConnorExperimentsSchema.EXPERIMENT_TYPE); - QueryUpdateService typeUpdateService = typeTable.getUpdateService(); - - int maxExpNumber = 0; - - // parse the sourceCollection into the target collection - for (Map databaseMap : sourceCollection) - { - Map map = new CaseInsensitiveHashMap<>(); - map.put("ExperimentNumber", databaseMap.get("expnumber")); - int expNumber = (int) databaseMap.get("expnumber"); - if (expNumber > form.getBeginRange() && expNumber < form.getEndRange()) - { - if (expNumber > maxExpNumber) - maxExpNumber = expNumber; - map.put("Name", databaseMap.get("expnumber")); - map.put("Description", databaseMap.get("expDescription")); - - String currentType = (String) databaseMap.get("expType"); - Integer targetType = null; - if (currentType != null) - { - TableSelector typeSelector = new TableSelector(typeTable, Collections.singleton("RowId"), new SimpleFilter(FieldKey.fromParts("Name"), currentType), null); - targetType = typeSelector.getObject(Integer.class); - if (targetType == null) - { - Map newType = new CaseInsensitiveHashMap<>(); - newType.put("Name", currentType); - newType.put("Enabled", true); - List> newTypes = typeUpdateService.insertRows(getUser(), getContainer(), Collections.singletonList(newType), new BatchValidationException(), null, null); - targetType = (Integer) newTypes.get(0).get("RowId"); - } - } - - map.put("ExperimentTypeId", targetType); - //map.put("Modified", databaseMap.get("created")); - - // get the user name - User effectiveUser; - if (databaseMap.get("initials") == null) - { - effectiveUser = getUser(); - } - else - { - User user = UserManager.getUserByDisplayName((String) databaseMap.get("initials")); - if (user == null) - { - LogManager.getLogger(OConnorExperimentsController.class).warn("User '" + databaseMap.get("initials") + "' not found for experiment " + databaseMap.get("expnumber")); - effectiveUser = getUser(); - } - else - { - effectiveUser = user; - } - } - - databaseMap.put("EffectiveUser", effectiveUser); - LogManager.getLogger(OConnorExperimentsController.class).info("Insert on experiment " + databaseMap.get("expnumber")); - List> updateResult; - try - { - updateResult = queryUpdateService.insertRows(getUser(), getContainer(), Collections.singletonList(map), batchErrors, null, null); - } - catch (Exception e) - { - // log the error to the logfile and continue - LogManager.getLogger(OConnorExperimentsController.class).warn("Error inserting expNumber " + expNumber + " with exception " + e.getMessage()); - continue; - } - if (batchErrors.hasErrors()) - { - // throw batchErrors.getLastRowError(); - LogManager.getLogger(OConnorExperimentsController.class).warn("Error inserting expNumber " + expNumber); - } - - Container workbookContainer = ContainerManager.getForId((String)updateResult.get(0).get("EntityId")); - databaseMap.put("ContainerObj", workbookContainer); - databaseMap.put("ContainerStr", updateResult.get(0).get("EntityId")); - - // We don't want these fields to be spoofable through the QueryUpdateService (and hence the Client API), - // so preserve the value from the source data manually - Date created = (Date)databaseMap.get("created"); - new SqlExecutor(CoreSchema.getInstance().getSchema()).execute("UPDATE core.containers SET CreatedBy = ?, Created = ? WHERE RowId = ?", effectiveUser.getUserId(), created, workbookContainer.getRowId()); - - // Move files - File sourceFile = new File(fileContentService.getFileRoot(sourceContainer).getPath() + File.separator + "@files", databaseMap.get("expnumber").toString()); - File targetDir = new File(fileContentService.getFileRoot(targetContainer).getPath() + File.separator + databaseMap.get("expnumber").toString() + File.separator + "@files"); - LogManager.getLogger(OConnorExperimentsController.class).info("Copy from file '" + sourceFile + "' to directory '" + targetDir +"'" ); - if (sourceFile.exists()) - { - FileUtils.copyDirectory(sourceFile, targetDir); - // only fire the File Move Event if this is a final migration - it is difficult to reverse - if (form.isFinalMigration()) - { - fileContentService.fireFileMoveEvent(sourceFile, targetDir, effectiveUser, getContainer()); - } - } - } - } - - // - // 2nd pass - update all ParentExperiment fields - // - for (Map databaseMap : sourceCollection) - { - int expNumber = (int) databaseMap.get("expnumber"); - if (expNumber > form.getBeginRange() && expNumber < form.getEndRange()) - { - Map map = new CaseInsensitiveHashMap<>(); - map.put("container", databaseMap.get("ContainerStr")); - - String[] parents; - ArrayList parentsEntityId = new ArrayList<>(); - if (databaseMap.get("expParent") != null) - { - String parentString = databaseMap.get("expParent").toString(); - parents = parentString.split("[\\s,;&]+"); - // get Container for SortOrder - for ( int i =0; i< parents.length; i++) - { - if ( ! parents[i].equalsIgnoreCase("and")) - { - Container child = targetContainer.getChild(parents[i]); - if (child != null) - { - parentsEntityId.add( child.getEntityId().toString() ); - } - else - { - LogManager.getLogger(OConnorExperimentsController.class).warn("child container not found: " + parents[i] + " for experiment " + databaseMap.get("expnumber") + " with username " + databaseMap.get("initials")); - } - } - } - if (!parentsEntityId.isEmpty()) - { - map.put("ParentExperiments", parentsEntityId.toArray(new String[0])); - - // workaround, pass user, container - databaseMap.get("Container"), singleton list - LogManager.getLogger(OConnorExperimentsController.class).info("Update rows on experiment " + databaseMap.get("expnumber")); - try - { - queryUpdateService.updateRows(getUser(), targetContainer, Collections.singletonList(map), null, null, null); - } - catch (Exception e) - { - // log the error to the logfile and continue - LogManager.getLogger(OConnorExperimentsController.class).warn("Error updating parent experiments for experiment number " + expNumber + " with exception " + e.getMessage()); - continue; - } - - } - } - } - } - - // - // 3rd pass update the wiki, done seperately so that the cache is not modified - // - for (Map databaseMap : sourceCollection) - { - int expNumber = (int) databaseMap.get("expnumber"); - if (expNumber > form.getBeginRange() && expNumber < form.getEndRange()) - { - - // Update the existing Wiki content with the value from the old table, if present - String wikiText = (String)databaseMap.get("expcomments"); - if (wikiText != null) - { - User user = UserManager.getUserByDisplayName((String) databaseMap.get("initials")); - User effectiveUser = user == null ? getUser() : user; - Container workbookContainer = targetContainer.getChild(databaseMap.get("expnumber").toString()); - if (workbookContainer == null) - { - LogManager.getLogger(OConnorExperimentsController.class).warn("Updating wiki, container not found: " + databaseMap.get("expnumber")); - } - else - { - Path path = new Path("_webdav").append(workbookContainer.getParsedPath()).append("@wiki", "default", "default.html"); - WebdavResolver resolver = ServiceRegistry.get(WebdavResolver.class); - WebdavResource resource = resolver.lookup(path); - - FileStream.StringFileStream in = new FileStream.StringFileStream(wikiText); - try - { - resource.copyFrom(effectiveUser, in); - } - catch (Exception e) - { - // log the error to the logfile and continue - LogManager.getLogger(OConnorExperimentsController.class).warn("Error wiki for experiment number " + expNumber + " with exception " + e.getMessage()); - continue; - } - finally - { - in.closeInputStream(); - } - LogManager.getLogger(OConnorExperimentsController.class).info("Inserting wiki for experiment " + databaseMap.get("expnumber")); - } - } - } - } - - // we need to leave the target location set up correctly so that the next experiment that's created gets the next value in the sequence - DbSequenceManager.get(targetContainer, ContainerManager.WORKBOOK_DBSEQUENCE_NAME).ensureMinimum(maxExpNumber); - - return true; - } - - @Override - public void addNavTrail(NavTree root) - { - } - - @Override - public ActionURL getSuccessURL(UserForm form) - { - UserSchema targetSchema = QueryService.get().getUserSchema(getUser(), getContainer(), OConnorExperimentsSchema.NAME); - return targetSchema.getQueryDefForTable(OConnorExperimentsSchema.EXPERIMENTS).urlFor(QueryAction.executeQuery); - } - } - - public static class UserForm extends ReturnUrlForm - { - private String _sourceProject; - private int _beginRange; - private int _endRange; - private boolean _finalMigration; - - public String getSourceProject() - { - return _sourceProject; - } - - public void setSourceProject(String sourceProject) - { - _sourceProject = sourceProject; - } - - public int getBeginRange() - { - return _beginRange; - } - - public void setBeginRange(int beginRange) - { - _beginRange = beginRange; - } - - public int getEndRange() - { - return _endRange; - } - - public void setEndRange(int endRange) - { - _endRange = endRange; - } - - public boolean isFinalMigration() - { - return _finalMigration; - } - - public void setFinalMigration(boolean finalMigration) - { - _finalMigration = finalMigration; - } - } - - /** * Use the QueryUpdateService to create a new experiment so the Experiment and Workbook defaults are used * then redirect to the newly created experiment begin page. diff --git a/OConnorExperiments/src/org/labkey/oconnorexperiments/view/migrateData.jsp b/OConnorExperiments/src/org/labkey/oconnorexperiments/view/migrateData.jsp deleted file mode 100644 index 9bacb186..00000000 --- a/OConnorExperiments/src/org/labkey/oconnorexperiments/view/migrateData.jsp +++ /dev/null @@ -1,41 +0,0 @@ -<% -/* - * Copyright (c) 2005-2014 Fred Hutchinson Cancer Research Center - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -%> -<%@ taglib prefix="labkey" uri="http://www.labkey.org/taglib" %> -<%@ page import="org.labkey.oconnorexperiments.OConnorExperimentsController"%> -<%@ page extends="org.labkey.api.jsp.JspBase" %> - -> -OConnor Experiments to Workbooks data migration -<%=formatMissedErrorsInTable("form", 2)%> - - Source Project Name: - - - - Experiment Number Range: - - - - - Final Migration: - - - - <%= button("Submit").submit(true) %> - - - diff --git a/genotyping/src/org/labkey/genotyping/GenotypingController.java b/genotyping/src/org/labkey/genotyping/GenotypingController.java index f215fab6..fbe0ac2f 100644 --- a/genotyping/src/org/labkey/genotyping/GenotypingController.java +++ b/genotyping/src/org/labkey/genotyping/GenotypingController.java @@ -25,7 +25,6 @@ import org.labkey.api.action.FormHandlerAction; import org.labkey.api.action.FormViewAction; import org.labkey.api.action.HasViewContext; -import org.labkey.api.action.MutatingApiAction; import org.labkey.api.action.QueryViewAction; import org.labkey.api.action.QueryViewAction.QueryExportForm; import org.labkey.api.action.ReturnUrlForm; @@ -40,9 +39,11 @@ import org.labkey.api.assay.actions.AssayRunsAction; import org.labkey.api.assay.actions.BaseAssayAction; import org.labkey.api.assay.actions.ProtocolIdForm; +import org.labkey.api.audit.AuditLogService; +import org.labkey.api.audit.AuditTypeEvent; +import org.labkey.api.audit.provider.ContainerAuditProvider; import org.labkey.api.data.ActionButton; import org.labkey.api.data.ButtonBar; -import org.labkey.api.data.ColumnInfo; import org.labkey.api.data.Container; import org.labkey.api.data.DataRegion; import org.labkey.api.data.DataRegionSelection; @@ -66,19 +67,14 @@ import org.labkey.api.pipeline.PipelineValidationException; import org.labkey.api.pipeline.browse.PipelinePathForm; import org.labkey.api.portal.ProjectUrls; -import org.labkey.api.query.CustomView; import org.labkey.api.query.FieldKey; import org.labkey.api.query.QueryService; import org.labkey.api.query.QuerySettings; import org.labkey.api.query.QueryView; import org.labkey.api.query.UserSchema; -import org.labkey.api.security.CSRF; import org.labkey.api.security.IgnoresTermsOfUse; -import org.labkey.api.security.RequiresNoPermission; import org.labkey.api.security.RequiresPermission; import org.labkey.api.security.User; -import org.labkey.api.security.UserManager; -import org.labkey.api.security.ValidEmail; import org.labkey.api.security.permissions.AdminPermission; import org.labkey.api.security.permissions.DeletePermission; import org.labkey.api.security.permissions.InsertPermission; @@ -88,13 +84,11 @@ import org.labkey.api.util.FileUtil; import org.labkey.api.util.MinorConfigurationException; import org.labkey.api.util.PageFlowUtil; -import org.labkey.api.util.Pair; import org.labkey.api.util.URLHelper; import org.labkey.api.util.logging.LogHelper; import org.labkey.api.view.ActionURL; import org.labkey.api.view.DataView; import org.labkey.api.view.DetailsView; -import org.labkey.api.view.HttpView; import org.labkey.api.view.JspView; import org.labkey.api.view.NavTree; import org.labkey.api.view.NotFoundException; @@ -121,7 +115,6 @@ import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; -import java.io.PrintWriter; import java.math.BigInteger; import java.net.MalformedURLException; import java.net.URL; @@ -131,14 +124,12 @@ import java.sql.SQLException; import java.util.Collection; import java.util.Collections; -import java.util.Date; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; -import java.util.SortedSet; import java.util.TreeMap; import java.util.TreeSet; @@ -352,6 +343,13 @@ public boolean handlePost(MatchesForm form, BindException errors) try { _count = GenotypingManager.get().deleteMatches(getContainer(), getUser(), form.getAnalysis(), matchIds); + + if (_count > 0) + { + AuditTypeEvent event = new AuditTypeEvent(ContainerAuditProvider.CONTAINER_AUDIT_EVENT, getContainer(), + "Deleted " + _count + " genotyping match(es) from analysis " + form.getAnalysis() + ": " + matchIds); + AuditLogService.get().addEvent(getUser(), event); + } } catch (IllegalStateException e) { @@ -995,9 +993,7 @@ public boolean handlePost(ImportReadsForm form, BindException errors) throws Exc return true; } - // Successful submission via the UI... redirect either to the pipeline status grid or analyze action - ActionURL pipelineURL = PageFlowUtil.urlProvider(PipelineUrls.class).urlBegin(getContainer()); - _successURL = form.getAnalyze() ? getAnalyzeURL(form.getRun(), pipelineURL) : pipelineURL; + _successURL = PageFlowUtil.urlProvider(PipelineUrls.class).urlBegin(getContainer());; return true; } @@ -1061,327 +1057,6 @@ public void addNavTrail(NavTree root) } - public static class AnalyzeForm extends ReturnUrlForm - { - private int _run; - private String _sequencesView; - private String _description; - private String _samples; - - public int getRun() - { - return _run; - } - - @SuppressWarnings({"UnusedDeclaration"}) - public void setRun(int run) - { - _run = run; - } - - public String getSequencesView() - { - return _sequencesView; - } - - @SuppressWarnings({"UnusedDeclaration"}) - public void setSequencesView(String sequencesView) - { - _sequencesView = sequencesView; - } - - public String getDescription() - { - return _description; - } - - @SuppressWarnings({"UnusedDeclaration"}) - public void setDescription(String description) - { - _description = description; - } - - public String getSamples() - { - return _samples; - } - - @SuppressWarnings({"UnusedDeclaration"}) - public void setSamples(String samples) - { - _samples = samples; - } - } - - - private ActionURL getAnalyzeURL(int runId, ActionURL cancelURL) - { - ActionURL url = new ActionURL(AnalyzeAction.class, getContainer()); - url.addParameter("run", runId); - url.addReturnUrl(cancelURL); - return url; - } - - - @RequiresPermission(InsertPermission.class) - public static class AnalyzeAction extends FormViewAction - { - @Override - public void validateCommand(AnalyzeForm target, Errors errors) - { - } - - @Override - public ModelAndView getView(AnalyzeForm form, boolean reshow, BindException errors) throws Exception - { - GenotypingRun run = GenotypingManager.get().getRun(getContainer(), form.getRun()); - - // Verify that galaxy properties are set before submitting job. This will throw NotFoundException if either URL or web API key isn't set. - // 12.1: relax this requirement... allow users to submit jobs without a galaxy server configured or available - //GalaxyUtils.get(getContainer(), getUser()); - - SortedSet views = new TreeSet<>((c1, c2) -> - { - String name1 = c1.getName(); - String name2 = c2.getName(); - - return (null == name1 ? DEFAULT_VIEW_PLACEHOLDER : name1).compareTo((null == name2 ? DEFAULT_VIEW_PLACEHOLDER : name2)); - }); - GenotypingSchema gs = GenotypingSchema.get(); - views.addAll(QueryService.get().getCustomViews(getUser(), getContainer(), getUser(), gs.getSchemaName(), gs.getSequencesTable().getName(), false)); - - Map> sampleMap = new TreeMap<>(); - - try (Results results = SampleManager.get().selectSamples(getContainer(), getUser(), run, "library_sample_name, library_sample_species, key", "creating an analysis")) - { - Map fieldMap = results.getFieldMap(); - ColumnInfo sampleNameColumn = getColumnInfo(fieldMap, "library_sample_name"); - ColumnInfo sampleSpeciesColumn = getColumnInfo(fieldMap, "library_sample_species"); - ColumnInfo keyColumn = getColumnInfo(fieldMap, "key"); - - while (results.next()) - { - String sampleName = (String) sampleNameColumn.getValue(results); - String species = (String) sampleSpeciesColumn.getValue(results); - int sampleId = (Integer) keyColumn.getValue(results); - sampleMap.put(sampleId, new Pair<>(sampleName, species)); - } - } - - return new JspView<>("/org/labkey/genotyping/view/analyze.jsp", new AnalyzeBean(views, sampleMap, form.getReturnActionURL()), errors); - } - - // Throws NotFoundException if column doesn't exist - private ColumnInfo getColumnInfo(Map fieldMap, String columnName) - { - ColumnInfo column = fieldMap.get(FieldKey.fromString(columnName)); - - if (null == column) - throw new NotFoundException("Expected to find a column named \"" + columnName + "\" in the samples query"); - - return column; - } - - @Override - public boolean handlePost(AnalyzeForm form, BindException errors) throws Exception - { - GenotypingRun run = GenotypingManager.get().getRun(getContainer(), form.getRun()); - if (run == null) - { - errors.rejectValue("run", ERROR_MSG, "No run found"); - return false; - } - - PipeRoot root = PipelineService.get().findPipelineRoot(getContainer()); - - FileLike readsFile = run.getWorkingDir().resolveChild(run.getFileName()); - ViewBackgroundInfo vbi = new ViewBackgroundInfo(getContainer(), getUser(), getViewContext().getActionURL()); - - String sequencesViewName = form.getSequencesView(); - String description = form.getDescription(); - String sequencesView = DEFAULT_VIEW_PLACEHOLDER.equals(sequencesViewName) ? null : sequencesViewName; - String samples = form.getSamples(); - if(samples == null) - { - errors.reject(ERROR_MSG, "Must provide a list of sample IDs"); - return false; - } - - Set sampleKeys; - String[] keys = samples.split(","); - sampleKeys = new HashSet<>(keys.length); - - for (String key : keys) - sampleKeys.add(Integer.parseInt(key)); - - GenotypingAnalysis analysis = GenotypingManager.get().createAnalysis(getContainer(), getUser(), run, description, sequencesView); - try - { - PipelineJob analysisJob = new SubmitAnalysisJob(vbi, root, readsFile, analysis, sampleKeys); - PipelineService.get().queueJob(analysisJob); - } - catch (MinorConfigurationException e) - { - errors.reject(ERROR_MSG, e.getMessage()); - return false; - } - - return true; - } - - @Override - public URLHelper getSuccessURL(AnalyzeForm analyzeForm) - { - return PageFlowUtil.urlProvider(PipelineUrls.class).urlBegin(getContainer()); - } - - @Override - public void addNavTrail(NavTree root) - { - root.addChild("Submit Analysis"); - } - } - - - public static class AnalyzeBean - { - private final SortedSet _sequencesViews; - private final Map> _sampleMap; - private final ActionURL _returnUrl; - - private AnalyzeBean(SortedSet sequenceViews, Map> sampleMap, ActionURL returnUrl) - { - _sequencesViews = sequenceViews; - _sampleMap = sampleMap; - _returnUrl = returnUrl; - } - - public SortedSet getSequencesViews() - { - return _sequencesViews; - } - - public Map> getSampleMap() - { - return _sampleMap; - } - - public ActionURL getReturnUrl() - { - return _returnUrl; - } - } - - - public static ActionURL getWorkflowCompleteURL(Container c, GenotypingAnalysis analysis) - { - ActionURL url = new ActionURL(WorkflowCompleteAction.class, c); - url.addParameter("analysis", analysis.getRowId()); - url.addParameter("path", analysis.getPath()); - return url; - } - - - @RequiresNoPermission - @CSRF(CSRF.Method.NONE) - public class WorkflowCompleteAction extends MutatingApiAction - { - @Override - public void validateForm(ImportAnalysisForm form, Errors errors) - { - if (null == form.getAnalysis()) - errors.reject(ERROR_MSG, "Must specify an analysis parameter"); - - if (null == form.getPath()) - errors.reject(ERROR_MSG, "Must specify a path parameter"); - } - - @Override - public Object execute(ImportAnalysisForm form, BindException errors) throws Exception - { - LOG.info("Galaxy signaled the completion of analysis " + form.getAnalysis()); - String message; - - // Send any exceptions back to the Galaxy task so it can log it as well. - String FAILURE_PREFACE = "Failed to queue import analysis job: "; - - try - { - FileLike analysisDir = FileSystemLike.getVerifiedFileLike(getContainer(), form.getPath()); - int analysisId = form.getAnalysis(); - - User user = getUser(); - if (user.isGuest()) - { - Properties props = GenotypingManager.get().readProperties(analysisDir); - String email = (String)props.get("user"); - - if (null != email) - { - // Possible that user doesn't exist or changed email (e.g., re-loading an old analysis) - User test = UserManager.getUser(new ValidEmail(email)); - - if (null != test) - user = test; - } - } - - importAnalysis(analysisId, analysisDir, user); - message = "Import analysis job queued at " + new Date(); - } - catch (FileNotFoundException fnf) - { - // Send back a vague, generic message in the case of all file-not-found-type problems, e.g., specified path is - // missing, isn't a directory, lacks a properties.xml file, or doesn't match the analysis table path. This - // prevents attackers from gaining any useful information about the file system. Log the more detailed message. - message = FAILURE_PREFACE + "Analysis path doesn't match import path (see system log for more details)"; - - // But log more detail to the administrator so they're aware - LOG.error(FAILURE_PREFACE + fnf.getMessage()); - } - catch (Exception e) - { - message = FAILURE_PREFACE + e.getMessage(); - LOG.error(message); - } - - // Plain text response back to Galaxy - sendPlainText(message); - - return null; - } - } - - - public static class ImportAnalysisForm - { - private Integer _analysis = null; - private String _path = null; - - public Integer getAnalysis() - { - return _analysis; - } - - @SuppressWarnings({"UnusedDeclaration"}) - public void setAnalysis(Integer analysis) - { - _analysis = analysis; - } - - public String getPath() - { - return _path; - } - - @SuppressWarnings({"UnusedDeclaration"}) - public void setPath(String path) - { - _path = path; - } - } - - @RequiresPermission(InsertPermission.class) public class ImportAnalysisAction extends FormHandlerAction { @@ -1838,47 +1513,22 @@ public ModelAndView getView(RunForm form, BindException errors) throws Exception if (null == _run) throw new NotFoundException("Run not found"); - final boolean allowAnalysis = GenotypingManager.SEQUENCE_PLATFORMS.LS454.toString().equals(_run.getPlatform()); - - ModelAndView readsView; - readsView = super.getView(form, errors); + ModelAndView readsView = super.getView(form, errors); // Just return the view in export case if (form.isExport()) return readsView; VBox vbox = new VBox(); - final ActionButton submitAnalysis = new ActionButton("Add Analysis", getAnalyzeURL(_run.getRowId(), getViewContext().getActionURL())); if (GenotypingManager.get().hasAnalyses(_run)) { - GenotypingAnalysesView analyses = new GenotypingAnalysesView(getViewContext(), null, "Analyses", new SimpleFilter(FieldKey.fromParts("Run"), _run.getRowId()), false) { - @Override - protected void populateButtonBar(DataView view, ButtonBar bar) - { - bar.add(submitAnalysis); - } - }; + GenotypingAnalysesView analyses = new GenotypingAnalysesView(getViewContext(), null, "Analyses", new SimpleFilter(FieldKey.fromParts("Run"), _run.getRowId()), false); analyses.setButtonBarPosition(DataRegion.ButtonBarPosition.TOP); analyses.setTitle("Analyses"); analyses.setTitleHref(getAnalysesURL(getContainer())); vbox.addView(analyses); } - else - { - vbox.addView(new HttpView() { - @Override - protected void renderInternal(Object model, PrintWriter out) throws Exception - { - if(allowAnalysis) - { - submitAnalysis.render(new RenderContext(getViewContext()), out); - out.println(""); - } - } - }); - } - vbox.addView(readsView); return vbox; @@ -1924,6 +1574,13 @@ public boolean handlePost(Object o, BindException errors) gm.deleteRun(run); } + if (!runs.isEmpty()) + { + AuditTypeEvent event = new AuditTypeEvent(ContainerAuditProvider.CONTAINER_AUDIT_EVENT, getContainer(), + "Deleted " + runs.size() + " genotyping run(s): " + runs); + AuditLogService.get().addEvent(getUser(), event); + } + return true; } @@ -1947,13 +1604,21 @@ public void validateCommand(Object target, Errors errors) public boolean handlePost(Object o, BindException errors) throws Exception { GenotypingManager gm = GenotypingManager.get(); + Set analysisIds = DataRegionSelection.getSelectedIntegers(getViewContext(), true); - for (Integer analysisId : DataRegionSelection.getSelectedIntegers(getViewContext(), true)) + for (Integer analysisId : analysisIds) { GenotypingAnalysis analysis = gm.getAnalysis(getContainer(), analysisId); gm.deleteAnalysis(analysis); } + if (!analysisIds.isEmpty()) + { + AuditTypeEvent event = new AuditTypeEvent(ContainerAuditProvider.CONTAINER_AUDIT_EVENT, getContainer(), + "Deleted " + analysisIds.size() + " genotyping analysis/analyses: " + analysisIds); + AuditLogService.get().addEvent(getUser(), event); + } + return true; } diff --git a/genotyping/src/org/labkey/genotyping/SubmitAnalysisJob.java b/genotyping/src/org/labkey/genotyping/SubmitAnalysisJob.java deleted file mode 100644 index f0102063..00000000 --- a/genotyping/src/org/labkey/genotyping/SubmitAnalysisJob.java +++ /dev/null @@ -1,333 +0,0 @@ -/* - * Copyright (c) 2010-2018 LabKey Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.labkey.genotyping; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; -import org.apache.commons.lang3.mutable.MutableInt; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.labkey.api.data.SimpleFilter; -import org.labkey.api.data.TSVWriter; -import org.labkey.api.data.Table; -import org.labkey.api.data.TableInfo; -import org.labkey.api.data.TableSelector; -import org.labkey.api.pipeline.PipeRoot; -import org.labkey.api.pipeline.PipelineJob; -import org.labkey.api.query.FieldKey; -import org.labkey.api.settings.AppProps; -import org.labkey.api.util.FileUtil; -import org.labkey.api.util.MinorConfigurationException; -import org.labkey.api.util.PageFlowUtil; -import org.labkey.api.util.URLHelper; -import org.labkey.api.view.NotFoundException; -import org.labkey.api.view.ViewBackgroundInfo; -import org.labkey.genotyping.galaxy.GalaxyServer; -import org.labkey.genotyping.galaxy.GalaxyUtils; -import org.labkey.genotyping.galaxy.WorkflowCompletionMonitor; -import org.labkey.genotyping.sequences.SequenceManager; -import org.labkey.vfs.FileLike; - -import java.io.File; -import java.io.IOException; -import java.net.URISyntaxException; -import java.sql.SQLException; -import java.util.HashMap; -import java.util.Map; -import java.util.Properties; -import java.util.Set; - -/** - * User: adam - * Date: Sep 10, 2010 - * Time: 9:43:21 PM - */ -public class SubmitAnalysisJob extends PipelineJob -{ - private final FileLike _dir; - private final GenotypingAnalysis _analysis; - private final FileLike _analysisDir; - private final Set _sampleIds; - - private URLHelper _galaxyURL = null; - private FileLike _completionFile = null; // Used for dev mode only - - // In dev mode only, we'll test the ability to connect to the Galaxy server once; if this connection fails, we'll - // skip trying to submit to Galaxy on subsequent attempts (until server restart). - private static Boolean _useGalaxy = null; - - @JsonCreator - protected SubmitAnalysisJob( - @JsonProperty("_dir") FileLike dir, - @JsonProperty("_analysis") GenotypingAnalysis analysis, - @JsonProperty("_analysisDir") FileLike analysisDir, - @JsonProperty("_sampleIds") Set sampleIds, - @JsonProperty("_galaxyURL") URLHelper galaxyURL, - @JsonProperty("_completionFile") FileLike completionFile, - @JsonProperty("_useGalaxy") Boolean useGalaxy - ) - { - _dir = dir; - _analysis = analysis; - _analysisDir = analysisDir; - _sampleIds = sampleIds; - _galaxyURL = galaxyURL; - _completionFile = completionFile; - _useGalaxy = useGalaxy; - } - - public SubmitAnalysisJob(ViewBackgroundInfo info, PipeRoot root, FileLike reads, GenotypingAnalysis analysis, @NotNull Set sampleIds) - { - super("Submit Analysis", info, root); // No pipeline provider - _dir = reads.getParent(); - _analysis = analysis; - _sampleIds = sampleIds; - - _analysisDir = _dir.resolveChild("analysis_" + _analysis.getRowId()); - - if (_analysisDir.exists()) - throw new MinorConfigurationException("Analysis directory already exists: " + _analysisDir.getPath()); - - try - { - _analysisDir.mkdir(); - } - catch(IOException e) - { - throw new MinorConfigurationException("Can't create analysis directory: " + _analysisDir.getPath()); - } - - setLogFile(_analysisDir.resolveChild(FileUtil.makeFileNameWithTimestamp("submit_analysis", "log")).toNioPathForWrite()); - info("Creating analysis directory: " + _analysisDir.getName()); - _analysis.setPath(FileUtil.getAbsolutePath(_analysisDir.toNioPathForRead())); - _analysis.setFileName(_analysisDir.getName()); - Table.update(getUser(), GenotypingSchema.get().getAnalysesTable(), PageFlowUtil.map("Path", _analysis.getPath(), "FileName", _analysis.getFileName()), _analysis.getRowId()); - } - - - @Override - public URLHelper getStatusHref() - { - return _galaxyURL; - } - - - @Override - public String getDescription() - { - return "Submit genotyping analysis " + _analysis.getRowId(); - } - - - @Override - public void run() - { - try - { - // Do this first to ensure that the Galaxy server is configured properly and the user has set a web API key - info("Verifying Galaxy configuration"); - GalaxyServer server = null; - - try - { - server = GalaxyUtils.get(getContainer(), getUser()); - } - catch (NotFoundException e) - { - warn("Can't submit to Galaxy server: " + e.getMessage()); - } - - writeAnalysisSamples(); - writeReads(); - writeProperties(server); - writeFasta(); - sendFilesToGalaxy(server); - monitorCompletion(); - if (!GenotypingManager.get().updateAnalysisStatus(_analysis, getUser(), Status.NotSubmitted, Status.Submitted)) - throw new IllegalStateException("Analysis status should be \"NotSubmitted\""); - info("Submitting genotyping analysis job complete"); - setStatus(TaskStatus.complete); - } - catch (Exception e) - { - error("Submitting genotyping analysis failed", e); - setStatus(TaskStatus.error); - } - } - - - private void writeAnalysisSamples() - { - Map sampleMap = new HashMap<>(); // Map to reuse for each insertion to AnalysisSamples - sampleMap.put("analysis", _analysis.getRowId()); - - for (Integer sampleId : _sampleIds) - { - sampleMap.put("sampleId", sampleId); - Table.insert(getUser(), GenotypingSchema.get().getAnalysisSamplesTable(), sampleMap); - } - } - - - private void writeReads() throws IOException - { - info("Writing reads file"); - setStatus("WRITING READS"); - - // Need a custom writer since TSVGridWriter does not work in background threads - try (TSVWriter writer = new TSVWriter() { - @Override - protected int write() - { - _pw.println("name\tsample\tsequence\tquality"); - - TableInfo ti = GenotypingSchema.get().getReadsTable(); - SimpleFilter filter = new SimpleFilter(FieldKey.fromParts("run"), _analysis.getRun()); - filter.addInClause(FieldKey.fromParts("SampleId"), _sampleIds); - MutableInt rows = new MutableInt(); - - new TableSelector(ti, ti.getColumns("name,sampleid,sequence,quality"), filter, null).forEach(rs -> { - _pw.println(rs.getString(1) + "\t" + rs.getInt(2) + "\t" + rs.getString(3) + "\t" + rs.getString(4)); - rows.increment(); - }); - - return rows.getValue(); - } - }) - { - writer.write(_analysisDir.resolveChild("reads.txt").toNioPathForWrite().toFile()); - } - } - - - private void writeProperties(@Nullable GalaxyServer server) throws IOException - { - info("Writing properties file"); - setStatus("WRITING PROPERTIES"); - Properties props = new Properties(); - props.put("url", GenotypingController.getWorkflowCompleteURL(getContainer(), _analysis).getURIString()); - props.put("dir", _analysisDir.getName()); - props.put("analysis", String.valueOf(_analysis.getRowId())); - props.put("user", getUser().getEmail()); - - // Tell Galaxy "workflow complete" task to write a file when the workflow is done. In many dev mode configurations - // the Galaxy server can't communicate via HTTP with LabKey Server, so watch for this file as a backup plan. - if (AppProps.getInstance().isDevMode() || null == server) - { - _completionFile = _analysisDir.resolveChild("analysis_complete.txt"); - - if (_completionFile.exists()) - throw new IllegalStateException("Completion file already exists: " + _completionFile.getPath()); - - props.put("completionFilename", _completionFile.getName()); - } - - GenotypingManager.get().writeProperties(props, _analysisDir); - } - - - private void writeFasta() throws IOException, SQLException - { - info("Writing FASTA file"); - setStatus("WRITING FASTA"); - File fastaFile = _analysisDir.resolveChild(GenotypingManager.SEQUENCES_FILE_NAME).toNioPathForWrite().toFile(); - SequenceManager.get().writeFasta(getContainer(), getUser(), _analysis.getSequencesView(), fastaFile); - } - - - private void sendFilesToGalaxy(GalaxyServer server) throws IOException, URISyntaxException - { - if (!shouldUseGalaxy(server)) - return; - - info("Sending files to Galaxy"); - setStatus("SENDING TO GALAXY"); - - try - { - GalaxyServer.DataLibrary library = server.createLibrary(_dir.getName() + "_" + _analysis.getRowId(), "MHC analysis " + _analysis.getRowId() + " for run " + _analysis.getRun(), "An MHC genotyping analysis"); - GalaxyServer.Folder root = library.getRootFolder(); - root.uploadFromImportDirectory(_dir.getName() + "/" + _analysisDir.getName(), "txt", null, true); - - _galaxyURL = library.getURL(); - - // Hack for testing without invoking the entire galaxy workflow: if it exists, link the matches.txt file - // in /matches into the data library. - if (AppProps.getInstance().isDevMode()) - { - FileLike matchesDir = _dir.resolveChild("matches"); - - if (matchesDir.exists()) - { - FileLike matchesFile = matchesDir.resolveChild(GenotypingManager.MATCHES_FILE_NAME); - - if (matchesFile.exists()) - root.uploadFromImportDirectory(_dir.getName() + "/matches", "txt", null, true); - } - } - } - catch (IOException e) - { - // Fail the job in production mode, but succeed in dev mode. This allows us to test in an environment - // where Galaxy is not reachable. - if (!AppProps.getInstance().isDevMode()) - throw e; - - info("Could not connect to Galaxy server", e); - } - } - - - private synchronized boolean shouldUseGalaxy(@Nullable GalaxyServer server) - { - // First time through - if (null == _useGalaxy) - { - if (null == server) - { - // Galaxy is not configured, so don't use it - _useGalaxy = false; - } - else if (!AppProps.getInstance().isDevMode()) - { - // With a Galaxy configuration in production mode, always try to connect to Galaxy server (even if failures occur) - _useGalaxy = true; - } - else - { - // In dev mode, attempt a connection now and if it fails skip subsequent connections - _useGalaxy = server.canConnect(); - - if (!_useGalaxy) - warn("Test connect to Galaxy server failed"); - } - } - else - { - if (!_useGalaxy && null != server) // We already warned in the null case - warn("Skipping submit to Galaxy server due to previous connection failure"); - } - - return _useGalaxy; - } - - // Wait until analysis is completely prepared and has been submitted to Galaxy before monitoring - private void monitorCompletion() - { - if (null != _completionFile) - WorkflowCompletionMonitor.get().monitor(_completionFile); - } -} diff --git a/genotyping/src/org/labkey/genotyping/view/analyze.jsp b/genotyping/src/org/labkey/genotyping/view/analyze.jsp deleted file mode 100644 index 0bc9e449..00000000 --- a/genotyping/src/org/labkey/genotyping/view/analyze.jsp +++ /dev/null @@ -1,195 +0,0 @@ -<% -/* - * Copyright (c) 2010-2016 LabKey Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -%> -<%@ page import="org.labkey.api.query.CustomView" %> -<%@ page import="org.labkey.api.util.Pair" %> -<%@ page import="org.labkey.api.view.template.ClientDependencies" %> -<%@ page import="org.labkey.genotyping.GenotypingController" %> -<%@ page import="org.labkey.genotyping.GenotypingController.AnalyzeBean" %> -<%@ page import="java.io.IOException" %> -<%@ page import="java.util.Map" %> -<%@ page import="java.util.SortedSet" %> -<%@ taglib prefix="labkey" uri="http://www.labkey.org/taglib" %> -<%@ page extends="org.labkey.api.jsp.JspBase" %> -<%! - @Override - public void addClientDependencies(ClientDependencies dependencies) - { - dependencies.add("clientapi/ext3"); - } -%> -<% - AnalyzeBean bean = (AnalyzeBean)getModelBean(); - -// ext-all.css hard-codes a white background, we want transparent instead %> - - - - -