diff --git a/.gitignore b/.gitignore index 3a2421a4c..bc2de3ae4 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,6 @@ resources/geome_secrets.json .idea/ build/ out/ +*.iml +*.ipr +*.iws diff --git a/JBD_readme.md b/JBD_readme.md new file mode 100644 index 000000000..d1a2710dc --- /dev/null +++ b/JBD_readme.md @@ -0,0 +1,18 @@ +# JBD Readme + +Random notes about working in the LIMS development environmeent + +# if we ever get class conflicts of the same class name, some steps to rectify: +NOTE: I believe this happens when i switch versions of geneious... doesn't seem to happen +if i stay on a single geneious version or with a single genious API. +there is probably a more elegant way to proceed here but this is what i've found helpful + * clean the Data directory or provide an entirely new one + * rm -fr /Library/Application\ Support/Geneious/plugins/com.biomatters.plugins.biocode.BiocodePlugin/ + * rm -fr ~/.gradle/caches + * may have to clean gradle like ./gradlew clean + +# Quickly create a plugin, without running time-consuming Tests... Only for development +# only run from the command line +``` +./gradlew quickCreatePlugin +``` diff --git a/PluginVersions.xml b/PluginVersions.xml index d8c868537..5b1626a13 100644 --- a/PluginVersions.xml +++ b/PluginVersions.xml @@ -1,9 +1,10 @@ - 3.0.9 - https://github.com/biocodellc/biocode-lims/releases/download/3.0.9/BiocodePlugin_3_0_9.gplugin - ==Biocode Plugin 3.0.9 - 6 March 2019== - * Fix Project search bug - * Add Genbank Metadata data properties to results list for Geome + 3.0.12 + https://github.com/biocodellc/biocode-lims/releases/download/3.0.12/BiocodePlugin_3_0_12.gplugin + ==Biocode Plugin 3.0.12 - 14 May 2019== + * Fixes bug in fetching tissues for plates + * Fixes "Already connected" exception occuring in Geneious v9 + * Changed behaviour when creating new extractions that use existing extractions ID's diff --git a/build.gradle b/build.gradle index 04937c8da..f3d2603e3 100644 --- a/build.gradle +++ b/build.gradle @@ -11,24 +11,22 @@ plugins { id 'net.saliman.properties' version '1.4.5' } -println "Gradle " + GradleVersion.current().getVersion() + ' - ' + GradleVersion.current().getBuildTime() + ' - ' + GradleVersion.current().getRevision() + (GradleVersion.current().isSnapshot() ? " SNAPSHOT" : "") +//println "Gradle " + GradleVersion.current().getVersion() + ' - ' + GradleVersion.current().getBuildTime() + ' - ' + GradleVersion.current().getRevision() + (GradleVersion.current().isSnapshot() ? " SNAPSHOT" : "") apply plugin: "java" apply plugin: "idea" -version = '3.0.9' +version = '3.0.12' defaultTasks 'createPlugin'; -task wrapper(type: Wrapper) { - gradleVersion = "3.5" -} - ext { shortName = "BiocodePlugin" pluginName = "com.biomatters.plugins.biocode." + shortName - geneiousPublicAPIVersion = "10.0.5" + geneiousPublicAPIVersion = "11.1.5" + //geneiousPublicAPIVersion = "2019.1.1" + //geneiousPublicAPIVersion = "9.0.5" pluginDistFilename = "${shortName}_" + version.replaceAll("\\.", "_") + ".gplugin" geneiousFilesName = "GeneiousFiles" geneiousFilesPath = "$buildDir/$geneiousFilesName" @@ -46,9 +44,11 @@ repositories { } dependencies { + compile group: "com.biomatters", name: "geneious.publicapi", version: "$geneiousPublicAPIVersion", configuration: "api", changing: true compile group: "com.google.guava", name: "guava", version:"15.0" + compile group: 'javax.xml.bind', name: 'jaxb-api', version: '2.3.1' compile group: "org.hsqldb", name:"hsqldb", version:"2.3.0" compile group: "org.jcommon", name:"jcommon", version:"1.0" compile group: "org.jfree", name:"jfreechart", version:"1.0.15" @@ -71,7 +71,10 @@ dependencies { compile group: "commons-beanutils", name:"commons-beanutils", version:"1.9.2" compile group: "mysql", name:"mysql-connector-java", version:"5.1.6" - + compile group: 'org.apache.httpcomponents', name: 'httpclient', version: '4.5.8' + compile group: 'commons-httpclient', name: 'commons-httpclient', version: '3.1' + compile group: 'org.jsslutils', name: 'jsslutils', version: '1.0.8' + testCompile "junit:junit:4.+" testRuntime group: "com.biomatters", name: "geneious.publicapi", version: "$geneiousPublicAPIVersion", configuration: "runtime" @@ -165,6 +168,21 @@ sourceSets { runtimeClasspath += fileTree("$geneiousFiles/lib") } } +task quickCreatePlugin ( dependsOn: assemble, type: Zip) { + description "Creates the gplugin to install into Geneious" + + from { + configurations.compile.filter { + !it.getName().startsWith("GeneiousPublicAPI-") && !it.getName().startsWith("jdom-") && !it.getName().startsWith("jebl-") + } + } + from(libsDir) + from(sourceSets.main.resources) + into "$pluginName" + + archiveName pluginDistFilename +} + task createPlugin(dependsOn: build, type: Zip) { description "Creates the gplugin to install into Geneious" @@ -181,6 +199,8 @@ task createPlugin(dependsOn: build, type: Zip) { } task runGeneious(dependsOn: [extractGeneiousRuntimeFiles, assemble], type: JavaExec) { + // NOTE, the expectation is that we java java 11 linked here + //executable '/usr/bin/java' main 'com.biomatters.iseek.application.ISeekMain' classpath sourceSets.main.runtimeClasspath args 'extraPlugins=' + pluginName diff --git a/development.md b/development.md new file mode 100644 index 000000000..37504c255 --- /dev/null +++ b/development.md @@ -0,0 +1,67 @@ +## Developers Section +This section is intended for developers interested in contributing to the Biocode LIMS development. + +## Requirements +* Java Development Kit 1.6+ +* Gradle + +### Geneious Plugin +Run the following command from the root directory to create the gplugin file: + + ./gradlew createPlugin + +Drag and drop the gplugin file from the build/distributoin directory into Geneious to install the plugin. + +### LIMS Server (Under Development) +The LIMS Server is packaged as a distributable WAR file that can be deployed on most Java Web Servers. (eg) Jetty +or Apache Tomcat. + +Run the following command from the **biocode-server** directory to create the war file: + + ./gradelw create-war + +The war file will be created in the biocode-server directory. + +## Development +The development of this project follows the [Gitflow branching strategy](https://www.atlassian.com/git/tutorials/comparing-workflows/gitflow-workflow). +All development is done in the develop branch and merged to master when complete. Thus master only contains released code. + +When switching branches it is always a good idea to run a build with + + ./gradlew build-plugin + +This will ensure any dependency changes for the new branch are applied and everything compiles. + +### Modules +The project currently contains two modules: + +* biocode-lims - The Geneious plugin +* biocode-server - The unreleased server + +In most cases you will only need to make changes to the biocode-lims source code. + + +### Dependency Management +The plugin uses Apache Ivy for depenedency management and the repository includes everything that is required for its use. + + +### Use of an IDE +Many modern IDEs come with Apache Ivy integration. If you are using such a feature, please note that the biocode-server +depends on some Geneious core classes and this is not reflected in the Ivy configuration. The main reason for this is + that the required libraries are not provided standalone in any repositories. + +In the gradle build, the complete Geneious runtime is downloaded and the required libraries are extracted from it. + +## Contributing +Please contact support@mooreabiocode.org + +## Biocode LIMS Server +The Biocode LIMS server is an extension to the original LIMS that adds security, user management, access control and +the ability to offload tasks from the Geneious client. + +The server needs to be deployed in a compatible Java Web Application server and acts as a middle man between the +client and the LIMS database. The client communicates with the server using a REST interface. By providing this +interface rather than a proprietary one, there is the possibility of other future clients outside of the Geneious +software package. + +It is currently in active development and has not been released yet. diff --git a/doc/labbench_11.0_mysql.sql b/doc/labbench_11.0_mysql.sql new file mode 100644 index 000000000..6459d0bbe --- /dev/null +++ b/doc/labbench_11.0_mysql.sql @@ -0,0 +1,570 @@ +-- MySQL Administrator dump 1.4 +-- +-- ------------------------------------------------------ +-- Server version 5.0.45-community-nt + + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8 */; + +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; + + +-- +-- You must first create a database and then "use" the database. +-- CAREFUL!! This script drops tables if they exist within the +-- database that you specify +-- + +-- +-- Definition of table `failure_reason` +-- + +DROP TABLE IF EXISTS `failure_reason`; +CREATE TABLE failure_reason ( + id INT AUTO_INCREMENT PRIMARY KEY, + name varchar(80), + description varchar(255) +) ENGINE=INNODB; + +-- +-- Definition of table `assembly` +-- + +DROP TABLE IF EXISTS `assembly`; +CREATE TABLE `assembly` ( + `id` int(10) unsigned NOT NULL auto_increment, + `extraction_id` varchar(45) NOT NULL, + `workflow` int(10) unsigned NOT NULL, + `progress` varchar(45) NOT NULL, + `consensus` longtext, + `params` longtext, + `coverage` float, + `disagreements` int(10) unsigned, + `edits` longtext, + `reference_seq_id` int(10) unsigned, + `confidence_scores` longtext, + `trim_params_fwd` longtext, + `trim_params_rev` longtext, + `other_processing_fwd` longtext, + `other_processing_rev` longtext, + `date` timestamp NOT NULL default CURRENT_TIMESTAMP, + `submitted` tinyint(1) NOT NULL default '0', + `editrecord` longtext, + `notes` longtext, + `technician` varchar(255), + `bin` varchar(255), + `ambiguities` int(10), + `failure_reason` int, + `failure_notes` longtext, + PRIMARY KEY (`id`), + KEY `FK_assembly_1` (`workflow`), + CONSTRAINT `FK_assembly_1` FOREIGN KEY (`workflow`) REFERENCES `workflow` (`id`), + KEY `FK_assembly_2` (`failure_reason`), + CONSTRAINT `FK_assembly_2` FOREIGN KEY (`failure_reason`) REFERENCES `failure_reason` (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=latin1; + +-- +-- Dumping data for table `assembly` +-- + +/*!40000 ALTER TABLE `assembly` DISABLE KEYS */; +/*!40000 ALTER TABLE `assembly` ENABLE KEYS */; + + +-- +-- Definition of table `cycle` +-- + +DROP TABLE IF EXISTS `cycle`; +CREATE TABLE `cycle` ( + `id` int(11) NOT NULL auto_increment, + `thermocycleId` int(11) default NULL, + `repeats` int(11) default NULL, + PRIMARY KEY (`id`), + KEY `thermocycleId` (`thermocycleId`), + CONSTRAINT `thermocycleId` FOREIGN KEY (`thermocycleId`) REFERENCES `thermocycle` (`id`) ON DELETE CASCADE ON UPDATE NO ACTION +) ENGINE=InnoDB AUTO_INCREMENT=29 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC; + +-- +-- Dumping data for table `cycle` +-- + +/*!40000 ALTER TABLE `cycle` DISABLE KEYS */; +INSERT INTO `cycle` (`id`,`thermocycleId`,`repeats`) VALUES + (7,15,1), + (8,15,25), + (9,15,1); +/*!40000 ALTER TABLE `cycle` ENABLE KEYS */; + + +-- +-- Definition of table `cyclesequencing` +-- + +DROP TABLE IF EXISTS `cyclesequencing`; +CREATE TABLE `cyclesequencing` ( + `id` int(11) NOT NULL auto_increment, + `primerName` varchar(64) NOT NULL, + `primerSequence` varchar(999) NOT NULL, + `notes` longtext NOT NULL, + `date` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP, + `workflow` int(10) unsigned default NULL, + `thermocycle` int(11) NOT NULL, + `plate` int(10) unsigned NOT NULL, + `location` int(11) NOT NULL, + `extractionId` varchar(45) NOT NULL, + `technician` varchar(90) NOT NULL, + `cocktail` int(10) unsigned default NULL, + `progress` varchar(45) NOT NULL, + `cleanupPerformed` tinyint(1) NOT NULL default '0', + `cleanupMethod` varchar(99) NOT NULL, + `direction` varchar(32) NOT NULL, + `gelimage` longblob, + PRIMARY KEY (`id`), + KEY `cycle_thermocycle` (`thermocycle`), + KEY `FK_cyclesequencing_plate` (`plate`), + KEY `FK_cyclesequencing_workflow` (`workflow`), + KEY `FK_cyclesequencing_cocktail` (`cocktail`), + CONSTRAINT `FK_cyclesequencing_cocktail` FOREIGN KEY (`cocktail`) REFERENCES `cyclesequencing_cocktail` (`id`), + CONSTRAINT `FK_cyclesequencing_plate` FOREIGN KEY (`plate`) REFERENCES `plate` (`id`), + CONSTRAINT `FK_cyclesequencing_workflow` FOREIGN KEY (`workflow`) REFERENCES `workflow` (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC; + +-- +-- Dumping data for table `cyclesequencing` +-- + +/*!40000 ALTER TABLE `cyclesequencing` DISABLE KEYS */; +/*!40000 ALTER TABLE `cyclesequencing` ENABLE KEYS */; + + +-- +-- Definition of table `cyclesequencing_cocktail` +-- + +DROP TABLE IF EXISTS `cyclesequencing_cocktail`; +CREATE TABLE `cyclesequencing_cocktail` ( + `id` int(10) unsigned NOT NULL auto_increment, + `name` varchar(99) NOT NULL, + `ddh2o` double NOT NULL, + `buffer` double NOT NULL, + `bigDye` double NOT NULL, + `notes` longtext NOT NULL, + `bufferConc` double NOT NULL, + `bigDyeConc` double NOT NULL, + `templateConc` double NOT NULL, + `primerConc` double NOT NULL, + `primerAmount` double NOT NULL, + `extraItem` mediumtext NOT NULL, + `extraItemAmount` double NOT NULL, + `templateAmount` double NOT NULL default '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8; + +-- +-- Dumping data for table `cyclesequencing_cocktail` +-- + +/*!40000 ALTER TABLE `cyclesequencing_cocktail` DISABLE KEYS */; +INSERT INTO `cyclesequencing_cocktail` (`id`,`name`,`ddh2o`,`buffer`,`bigDye`,`notes`,`bufferConc`,`bigDyeConc`,`templateConc`,`primerConc`,`primerAmount`,`extraItem`,`extraItemAmount`) VALUES + (0,'No Cocktail',0,0,0,' ',0,0,0,0,0,' ',0); +/*!40000 ALTER TABLE `cyclesequencing_cocktail` ENABLE KEYS */; + + +-- +-- Definition of table `cyclesequencing_thermocycle` +-- + +DROP TABLE IF EXISTS `cyclesequencing_thermocycle`; +CREATE TABLE `cyclesequencing_thermocycle` ( + `id` int(11) NOT NULL auto_increment, + `cycle` int(11) NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8; + +-- +-- Dumping data for table `cyclesequencing_thermocycle` +-- + +/*!40000 ALTER TABLE `cyclesequencing_thermocycle` DISABLE KEYS */; +INSERT INTO `cyclesequencing_thermocycle` (`id`,`cycle`) VALUES + (1,15); +/*!40000 ALTER TABLE `cyclesequencing_thermocycle` ENABLE KEYS */; + + +-- +-- Definition of table `databaseversion` +-- + +DROP TABLE IF EXISTS `databaseversion`; +CREATE TABLE `databaseversion` ( + `version` int(10) unsigned NOT NULL, + PRIMARY KEY (`version`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- +-- Dumping data for table `databaseversion` +-- + +/*!40000 ALTER TABLE `databaseversion` DISABLE KEYS */; +INSERT INTO `databaseversion` (`version`) VALUES + (11); +/*!40000 ALTER TABLE `databaseversion` ENABLE KEYS */; + + +-- +-- Definition of table `extraction` +-- + +DROP TABLE IF EXISTS `extraction`; +CREATE TABLE `extraction` ( + `id` int(10) unsigned NOT NULL auto_increment, + `date` timestamp NOT NULL default CURRENT_TIMESTAMP, + `method` varchar(45) NOT NULL, + `volume` double NOT NULL, + `dilution` double default NULL, + `concentrationStored` tinyint(1) NOT NULL default '0', + `concentration` double NOT NULL default '0', + `parent` varchar(45) NOT NULL, + `technician` varchar(90) NOT NULL, + `sampleId` varchar(45) NOT NULL, + `extractionId` varchar(45) NOT NULL, + `control` varchar(45) NOT NULL, + `plate` int(10) unsigned NOT NULL, + `location` int(10) unsigned NOT NULL, + `notes` longtext NOT NULL, + `gelimage` longblob, + `extractionBarcode` varchar(45) NOT NULL, + `previousPlate` varchar(45) NOT NULL, + `previousWell` varchar(45) NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `ind_extraction_3` (`extractionId`), + KEY `FK_extraction_plate` (`plate`) +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=latin1; + +-- +-- Dumping data for table `extraction` +-- + +/*!40000 ALTER TABLE `extraction` DISABLE KEYS */; +/*!40000 ALTER TABLE `extraction` ENABLE KEYS */; + + +-- +-- Definition of table `gelimages` +-- + +DROP TABLE IF EXISTS `gelimages`; +CREATE TABLE `gelimages` ( + `id` int(10) unsigned NOT NULL auto_increment, + `name` VARCHAR(45) DEFAULT 'Image' NOT NULL, + `plate` int(11) NOT NULL default '0', + `imageData` longblob, + `notes` longtext NOT NULL, + PRIMARY KEY (`id`), + KEY `pcrImage` (`plate`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- +-- Dumping data for table `gelimages` +-- + +/*!40000 ALTER TABLE `gelimages` DISABLE KEYS */; +/*!40000 ALTER TABLE `gelimages` ENABLE KEYS */; + + +-- +-- Definition of table `pcr` +-- + +DROP TABLE IF EXISTS `pcr`; +CREATE TABLE `pcr` ( + `id` int(11) NOT NULL auto_increment, + `prName` varchar(64) default NULL, + `prSequence` varchar(999) default NULL, + `date` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP, + `workflow` int(10) unsigned default NULL, + `plate` int(10) unsigned NOT NULL, + `location` int(11) NOT NULL, + `cocktail` int(10) unsigned NOT NULL, + `progress` varchar(45) NOT NULL, + `extractionId` varchar(45) NOT NULL, + `technician` varchar(90) NOT NULL, + `thermocycle` int(11) NOT NULL default '-1', + `cleanupPerformed` tinyint(1) NOT NULL default '0', + `cleanupMethod` varchar(45) NOT NULL, + `notes` longtext NOT NULL, + `revPrName` varchar(64) NOT NULL, + `revPrSequence` varchar(999) NOT NULL, + `gelimage` longblob, + PRIMARY KEY (`id`), + KEY `ind_pcr_plate` (`plate`), + KEY `FK_pcr_workflow` (`workflow`), + KEY `FK_pcr_cocktail` (`cocktail`), + CONSTRAINT `FK_pcr_cocktail` FOREIGN KEY (`cocktail`) REFERENCES `pcr_cocktail` (`id`), + CONSTRAINT `FK_pcr_plate` FOREIGN KEY (`plate`) REFERENCES `plate` (`id`), + CONSTRAINT `FK_pcr_workflow` FOREIGN KEY (`workflow`) REFERENCES `workflow` (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC; + +-- +-- Dumping data for table `pcr` +-- + +/*!40000 ALTER TABLE `pcr` DISABLE KEYS */; +/*!40000 ALTER TABLE `pcr` ENABLE KEYS */; + + +-- +-- Definition of table `pcr_cocktail` +-- + +DROP TABLE IF EXISTS `pcr_cocktail`; +CREATE TABLE `pcr_cocktail` ( + `id` int(10) unsigned NOT NULL auto_increment, + `name` varchar(99) NOT NULL, + `ddH20` double NOT NULL, + `buffer` double NOT NULL, + `mg` double NOT NULL, + `bsa` double NOT NULL, + `dNTP` double NOT NULL, + `taq` double NOT NULL, + `notes` longtext NOT NULL, + `bufferConc` double NOT NULL, + `mgConc` double NOT NULL, + `dNTPConc` double NOT NULL, + `taqConc` double NOT NULL, + `templateConc` double NOT NULL, + `bsaConc` double NOT NULL, + `fwPrAmount` double NOT NULL, + `fwPrConc` double NOT NULL, + `revPrAmount` double NOT NULL, + `revPrConc` double NOT NULL, + `extraItem` mediumtext NOT NULL, + `extraItemAmount` double NOT NULL, + `templateAmount` double NOT NULL default '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=17 DEFAULT CHARSET=utf8; + +-- +-- Dumping data for table `pcr_cocktail` +-- + +/*!40000 ALTER TABLE `pcr_cocktail` DISABLE KEYS */; +INSERT INTO `pcr_cocktail` (`id`,`name`,`ddH20`,`buffer`,`mg`,`bsa`,`dNTP`,`taq`,`notes`,`bufferConc`,`mgConc`,`dNTPConc`,`taqConc`,`templateConc`,`bsaConc`,`fwPrAmount`,`fwPrConc`,`revPrAmount`,`revPrConc`,`extraItem`,`extraItemAmount`) VALUES + (0,'No Cocktail',0,0,0,0,0,0,' ',0,0,0,0,0,0,0,0,0,0,' ',0); +/*!40000 ALTER TABLE `pcr_cocktail` ENABLE KEYS */; + + +-- +-- Definition of table `pcr_thermocycle` +-- + +DROP TABLE IF EXISTS `pcr_thermocycle`; +CREATE TABLE `pcr_thermocycle` ( + `id` int(11) NOT NULL auto_increment, + `cycle` int(11) default NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8; + +-- +-- Dumping data for table `pcr_thermocycle` +-- + +/*!40000 ALTER TABLE `pcr_thermocycle` DISABLE KEYS */; +INSERT INTO `pcr_thermocycle` (`id`,`cycle`) VALUES + (2,15); +/*!40000 ALTER TABLE `pcr_thermocycle` ENABLE KEYS */; + + +-- +-- Definition of table `plate` +-- + +DROP TABLE IF EXISTS `plate`; +CREATE TABLE `plate` ( + `id` int(10) unsigned NOT NULL auto_increment, + `name` varchar(64) NOT NULL default 'plate', + `date` date NOT NULL, + `size` int(11) NOT NULL, + `type` varchar(45) NOT NULL, + `thermocycle` int(11) NOT NULL default '-1', + PRIMARY KEY USING BTREE (`id`,`name`) +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8; + +-- +-- Dumping data for table `plate` +-- + +/*!40000 ALTER TABLE `plate` DISABLE KEYS */; +/*!40000 ALTER TABLE `plate` ENABLE KEYS */; + + +-- +-- Definition of table `state` +-- + +DROP TABLE IF EXISTS `state`; +CREATE TABLE `state` ( + `id` int(11) NOT NULL auto_increment, + `temp` int(11) unsigned NOT NULL, + `length` int(11) unsigned NOT NULL, + `cycleId` int(11) default NULL, + PRIMARY KEY (`id`), + KEY `cycleId` (`cycleId`), + CONSTRAINT `cycleId` FOREIGN KEY (`cycleId`) REFERENCES `cycle` (`id`) ON DELETE CASCADE ON UPDATE NO ACTION +) ENGINE=InnoDB AUTO_INCREMENT=20 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC; + +-- +-- Dumping data for table `state` +-- + +/*!40000 ALTER TABLE `state` DISABLE KEYS */; +INSERT INTO `state` (`id`,`temp`,`length`,`cycleId`) VALUES + (15,20,60,7), + (16,90,30,8), + (17,55,120,8), + (18,75,30,8), + (19,20,540,9); +/*!40000 ALTER TABLE `state` ENABLE KEYS */; + + +-- +-- Definition of table `thermocycle` +-- + +DROP TABLE IF EXISTS `thermocycle`; +CREATE TABLE `thermocycle` ( + `id` int(11) NOT NULL auto_increment, + `name` varchar(64) default NULL, + `notes` longtext NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=16 DEFAULT CHARSET=utf8; + +-- +-- Dumping data for table `thermocycle` +-- + +/*!40000 ALTER TABLE `thermocycle` DISABLE KEYS */; +INSERT INTO `thermocycle` (`id`,`name`,`notes`) VALUES + (15,'Example Thermocycle','An example cycle...'); +/*!40000 ALTER TABLE `thermocycle` ENABLE KEYS */; + + +-- +-- Definition of table `workflow` +-- + +DROP TABLE IF EXISTS `workflow`; +CREATE TABLE `workflow` ( + `id` int(10) unsigned NOT NULL auto_increment, + `name` varchar(45) NOT NULL default 'workflow', + `date` date NOT NULL, + `extractionId` int(10) unsigned NOT NULL, + `locus` varchar(45) NOT NULL default 'COI', + PRIMARY KEY USING BTREE (`id`), + UNIQUE KEY `ind_workflow_3` (`name`), + KEY `FK_workflow_extraction` (`extractionId`), + CONSTRAINT `FK_workflow_extraction` FOREIGN KEY (`extractionId`) REFERENCES `extraction` (`id`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8; + +-- +-- Dumping data for table `workflow` +-- + +/*!40000 ALTER TABLE `workflow` DISABLE KEYS */; +/*!40000 ALTER TABLE `workflow` ENABLE KEYS */; + +-- +-- Definition of table `traces` +-- +DROP TABLE IF EXISTS `traces`; +CREATE TABLE `traces` ( + `id` int(10) unsigned NOT NULL auto_increment, + `reaction` int(11) NOT NULL, + `name` varchar(96) NOT NULL, + `data` longblob NOT NULL, + PRIMARY KEY (`id`), + KEY `FK_traces_1` (`reaction`), + CONSTRAINT `FK_traces_1` FOREIGN KEY (`reaction`) REFERENCES `cyclesequencing` (`id`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=latin1; + + +-- +-- Definition of table `properties` +-- +CREATE TABLE properties ( + name VARCHAR(255) PRIMARY KEY, + value VARCHAR(255) +) ENGINE=INNODB; + +-- +-- Dumping data for table `properties` +-- +INSERT INTO properties (name,value) VALUES + ('fullDatabaseVersion', '11.0'); + +-- +-- Definition of table `sequencing_result` +-- +CREATE TABLE sequencing_result ( + reaction INT(11) NULL, + assembly INT(10) UNSIGNED NULL, + PRIMARY KEY (reaction, assembly), + FOREIGN KEY(reaction) REFERENCES cyclesequencing(id) ON DELETE CASCADE, + FOREIGN KEY(assembly) REFERENCES assembly(id) ON DELETE CASCADE +) ENGINE=INNODB; + +CREATE TABLE gel_quantification ( + id int(10) UNSIGNED NOT NULL AUTO_INCREMENT, + `date` timestamp NOT NULL default CURRENT_TIMESTAMP, + extractionId INT(10) UNSIGNED NOT NULL, + plate int(10) UNSIGNED NOT NULL, + location int(10) UNSIGNED NOT NULL, + technician VARCHAR(255), + notes LONGTEXT, + volume DOUBLE, + gelImage longblob, + gelBuffer VARCHAR(255), + gelConc DOUBLE, + stain VARCHAR(255), + stainConc VARCHAR(255), + stainMethod VARCHAR(255), + gelLadder VARCHAR(255), + threshold INTEGER, + aboveThreshold INTEGER, + PRIMARY KEY (id), + FOREIGN KEY (extractionId) REFERENCES extraction(id) ON DELETE CASCADE , + FOREIGN KEY (plate) REFERENCES plate(id) ON DELETE CASCADE +) ENGINE=InnoDB AUTO_INCREMENT=1; + +/*!40000 ALTER TABLE `traces` DISABLE KEYS */; +/*!40000 ALTER TABLE `traces` ENABLE KEYS */; + + + + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; + +CREATE INDEX plate_name ON plate (name); +CREATE INDEX workflow_date ON workflow (date); +CREATE INDEX workflow_locus ON workflow (locus); +CREATE INDEX plate_type ON plate (type); +CREATE INDEX plate_date ON plate (date); +CREATE INDEX extraction_extractionBarcode ON extraction (extractionBarcode); +CREATE INDEX extraction_date ON extraction (date); +CREATE INDEX assembly_progress ON assembly (progress); +CREATE INDEX assembly_submitted ON assembly (submitted); +CREATE INDEX assembly_technician ON assembly (technician); +CREATE INDEX assembly_date ON assembly (date); +CREATE INDEX extraction_sampleId ON extraction (sampleId); diff --git a/doc/release_notes.txt b/doc/release_notes.txt index f7fb33ff4..c3cf9811b 100644 --- a/doc/release_notes.txt +++ b/doc/release_notes.txt @@ -1,3 +1,14 @@ +==Biocode Plugin 3.0.11 - 14 May 2019== +* Fixes bug in fetching tissues for plates +* Fixes "Already connected" exception occuring in Geneious v9 +* Changed behaviour when creating new extractions that use existing extractions ID's + +==Biocode Plugin 3.0.11 - 5 April 2019== +* Fixes error in fetching REF assemblies + +==Biocode Plugin 3.0.10 - 26 March 2019== +* Enabled OR queries for Geome + ==Biocode Plugin 3.0.9 - 6 March 2019== * Fixed bug where queries on Geome Project were crashing application * Added genbankCountry, genbankDate, and genbankLatLng to output data properties diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 299769e9a..74aa43d43 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-3.5-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.3-all.zip diff --git a/readme.md b/readme.md index 420ecc43c..e852b33e4 100644 --- a/readme.md +++ b/readme.md @@ -1,76 +1,13 @@ # Biocode LIMS -The Biocode LIMS is a Geneious plugin that was developed as part of the [Moorea Biocode Project](http://mooreabiocode.org). -It comprises everything you need to manage your lab and sequence analysis workflows. - -### Biocode LIMS Server -The Biocode LIMS server is an extension to the original LIMS that adds security, user management, access control and -the ability to offload tasks from the Geneious client. - -The server needs to be deployed in a compatible Java Web Application server and acts as a middle man between the -client and the LIMS database. The client communicates with the server using a REST interface. By providing this -interface rather than a proprietary one, there is the possibility of other future clients outside of the Geneious -software package. - -It is currently in active development and has not been released yet. + +The Biocode LIMS is a Geneious plugin that comprises everything you need to manage your lab and sequence analysis workflows. For information on how to use the Biocode LIMS and to download the plugin visit our [Wiki Page](https://github.com/biocodellc/biocode-lims/wiki). ## Useful Information -* Official releases are available from the [official website](https://github.com/biocodellc/biocode-lims/releases) -* Official wiki at [https://github.com/biocodellc/biocode-lims/](https://github.com/biocodellc/biocode-lims/) for more information +* Official releases are available from our [releases page](https://github.com/biocodellc/biocode-lims/releases) +* Official [Wiki Page](https://github.com/biocodellc/biocode-lims/wiki) for more information including the user guide. -* Discussion forum at [http://connect.barcodeoflife.net/group/lims](http://connect.barcodeoflife.net/group/lims) * Support email support@mooreabiocode.org +* Information for developers on contributing to the Biocode LIMS plugin is on our [development page](https://github.com/biocodellc/biocode-lims/blob/develop/development.md) -## Requirements -* Java Development Kit 1.6+ -* Gradle - -## Installation -### Geneious Plugin -Run the following command from the root directory to create the gplugin file: - - ./gradlew createPlugin - -Drag and drop the gplugin file from the build/distributoin directory into Geneious to install the plugin. - -### LIMS Server (Under Development) -The LIMS Server is packaged as a distributable WAR file that can be deployed on most Java Web Servers. (eg) Jetty -or Apache Tomcat. - -Run the following command from the **biocode-server** directory to create the war file: - - ./gradelw create-war - -The war file will be created in the biocode-server directory. - -## Development -The development of this project follows the [Gitflow branching strategy](https://www.atlassian.com/git/tutorials/comparing-workflows/gitflow-workflow). -All development is done in the develop branch and merged to master when complete. Thus master only contains released code. - -When switching branches it is always a good idea to run a build with - - ./gradlew build-plugin - -This will ensure any dependency changes for the new branch are applied and everything compiles. - -### Modules -The project currently contains two modules: - -* biocode-lims - The Geneious plugin -* biocode-server - The unreleased server - -In most cases you will only need to make changes to the biocode-lims source code. - - -### Dependency Management -The plugin uses Apache Ivy for depenedency management and the repository includes everything that is required for its use. - - -### Use of an IDE -Many modern IDEs come with Apache Ivy integration. If you are using such a feature, please note that the biocode-server -depends on some Geneious core classes and this is not reflected in the Ivy configuration. The main reason for this is - that the required libraries are not provided standalone in any repositories. -In the gradle build, the complete Geneious runtime is downloaded and the required libraries are extracted from it. -## Contributing -Please contact support@mooreabiocode.org diff --git a/src/com/biomatters/plugins/biocode/BiocodePlugin.java b/src/com/biomatters/plugins/biocode/BiocodePlugin.java index 0a641b511..32a955bce 100644 --- a/src/com/biomatters/plugins/biocode/BiocodePlugin.java +++ b/src/com/biomatters/plugins/biocode/BiocodePlugin.java @@ -47,7 +47,7 @@ public class BiocodePlugin extends GeneiousPlugin { } private static GeneiousActionOptions superBiocodeAction; - public static final String PLUGIN_VERSION = "3.0.9"; + public static final String PLUGIN_VERSION = "3.0.12"; public static final String SUPPORT_EMAIL = "support@mooreabiocode.org"; public static GeneiousActionOptions getSuperBiocodeAction() { diff --git a/src/com/biomatters/plugins/biocode/labbench/BiocodeService.java b/src/com/biomatters/plugins/biocode/labbench/BiocodeService.java index 9fa78d345..c22f5e669 100644 --- a/src/com/biomatters/plugins/biocode/labbench/BiocodeService.java +++ b/src/com/biomatters/plugins/biocode/labbench/BiocodeService.java @@ -302,10 +302,10 @@ public static Map getSearchDownloadOptions(boolean tissues, bool public static FIMSConnection[] getFimsConnections() { return new FIMSConnection[] { new ExcelFimsConnection(), - new FusionTablesFimsConnection(), + //new FusionTablesFimsConnection(), new MySQLFimsConnection(), new MooreaFimsConnection(), - new TAPIRFimsConnection(), + //new TAPIRFimsConnection(), new BiocodeFIMSConnection(), new geomeFIMSConnection() }; @@ -721,6 +721,18 @@ public void addDatabaseServiceListener(DatabaseServiceListener listener) { super.addDatabaseServiceListener(listener); listener.searchableStatusChanged(isLoggedIn, loggedOutMessage); } + + /** + * + * @param urns + * @param callback + * 11 geneious reports this error having to do with urns: contained + * in XML responses and requiring this method. However, JBD is fairly certain nothing + * needs to be done with this information. + */ + public void retrieve(URN[] urns, RetrieveCallback callback) { + //do nothing + } public void retrieve(Query query, RetrieveCallback callback, URN[] urnsToNotRetrieve) throws DatabaseServiceException { retrieve(query, callback, urnsToNotRetrieve, false); diff --git a/src/com/biomatters/plugins/biocode/labbench/fims/MooreaFimsConnection.java b/src/com/biomatters/plugins/biocode/labbench/fims/MooreaFimsConnection.java index 8acf21a20..1c04f6851 100644 --- a/src/com/biomatters/plugins/biocode/labbench/fims/MooreaFimsConnection.java +++ b/src/com/biomatters/plugins/biocode/labbench/fims/MooreaFimsConnection.java @@ -68,7 +68,7 @@ public String getName() { } public String getDescription() { - return "A connection to the Moorea FIMS database"; + return "Connection to Moorea Biocode (FIMS1) at http://biocode.berkeley.edu/"; } public PasswordOptions getConnectionOptions() { diff --git a/src/com/biomatters/plugins/biocode/labbench/fims/TableFimsConnection.java b/src/com/biomatters/plugins/biocode/labbench/fims/TableFimsConnection.java index 79de59994..4bb8c9d77 100644 --- a/src/com/biomatters/plugins/biocode/labbench/fims/TableFimsConnection.java +++ b/src/com/biomatters/plugins/biocode/labbench/fims/TableFimsConnection.java @@ -136,7 +136,7 @@ List getFieldsFromMultipleOptions(TableFimsConnectionOptions opti public DocumentField getTableCol(String colName) { if(fields != null) { for(DocumentField field : fields) { - if(field.getCode().equals(colName)) { + if(field.getCode().equalsIgnoreCase(colName)) { return field; } } diff --git a/src/com/biomatters/plugins/biocode/labbench/fims/biocode/BiocodeFIMSConnection.java b/src/com/biomatters/plugins/biocode/labbench/fims/biocode/BiocodeFIMSConnection.java index f39d495f0..5307c9c50 100644 --- a/src/com/biomatters/plugins/biocode/labbench/fims/biocode/BiocodeFIMSConnection.java +++ b/src/com/biomatters/plugins/biocode/labbench/fims/biocode/BiocodeFIMSConnection.java @@ -52,7 +52,7 @@ private static boolean serverProbablyDeployed() { @Override public String getLabel() { - return "Biocode FIMS"; + return "BiSciCol FIMS"; } @Override @@ -62,7 +62,7 @@ public String getName() { @Override public String getDescription() { - return "Connection to the new Biocode FIMS (https://github.com/biocodellc/biocode-fims)"; + return "Connection to BiSciCol (FIMS2) at https://www.biscicol.org/"; } private Map> cachedSamples = new HashMap>(); diff --git a/src/com/biomatters/plugins/biocode/labbench/fims/geome/geomeFIMSClient.java b/src/com/biomatters/plugins/biocode/labbench/fims/geome/geomeFIMSClient.java index 2b2d4a582..f7eca8c52 100644 --- a/src/com/biomatters/plugins/biocode/labbench/fims/geome/geomeFIMSClient.java +++ b/src/com/biomatters/plugins/biocode/labbench/fims/geome/geomeFIMSClient.java @@ -1,8 +1,10 @@ package com.biomatters.plugins.biocode.labbench.fims.geome; import com.biomatters.geneious.publicapi.databaseservice.DatabaseServiceException; -import com.biomatters.plugins.biocode.BiocodePlugin; import com.biomatters.plugins.biocode.labbench.fims.biocode.*; +import org.apache.http.conn.ssl.TrustStrategy; +import org.apache.http.ssl.SSLContexts; + import org.codehaus.jackson.JsonNode; import org.codehaus.jackson.map.ObjectMapper; import org.glassfish.jersey.client.ClientConfig; @@ -12,13 +14,11 @@ import org.glassfish.jersey.jackson.JacksonFeature; import org.glassfish.jersey.message.GZipEncoder; +import javax.net.ssl.SSLContext; import javax.ws.rs.NotFoundException; import javax.ws.rs.ProcessingException; import javax.ws.rs.WebApplicationException; -import javax.ws.rs.client.ClientBuilder; -import javax.ws.rs.client.Entity; -import javax.ws.rs.client.Invocation; -import javax.ws.rs.client.WebTarget; +import javax.ws.rs.client.*; import javax.ws.rs.core.Form; import javax.ws.rs.core.GenericType; import javax.ws.rs.core.MediaType; @@ -29,11 +29,16 @@ import java.lang.reflect.InvocationTargetException; import java.net.MalformedURLException; import java.net.URLEncoder; +import java.security.KeyManagementException; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.logging.Logger; +import com.biomatters.plugins.biocode.BiocodePlugin; + /** * @author Matthew Cheung * Created on 7/02/14 5:51 AM @@ -52,8 +57,26 @@ public geomeFIMSClient(String hostname, int timeout) { ClientConfig config = new ClientConfig() .property(ClientProperties.CONNECT_TIMEOUT, timeout * 1000) .property(ClientProperties.READ_TIMEOUT, timeout * 1000); + + ClientBuilder builder = ClientBuilder.newBuilder(); + + // Create SSL Socket Factory Explicitly for older versions of geneious + // This is a work-around where we accept the certificate for all geome connections + TrustStrategy acceptingTrustStrategy = (cert, authType) -> true; + SSLContext sslContext = null; + try { + sslContext = SSLContexts.custom().loadTrustMaterial(null, acceptingTrustStrategy).build(); + } catch (NoSuchAlgorithmException e) { + throw new ProcessingException(e.getMessage()); + } catch (KeyManagementException e) { + throw new ProcessingException(e.getMessage()); + } catch (KeyStoreException e) { + throw new ProcessingException(e.getMessage()); + } + builder.sslContext(sslContext); - target = ClientBuilder.newBuilder().withConfig(config).build().target(hostname) + ClientBuilder builderConfig = builder.withConfig(config); + target = builderConfig.build().target(hostname) .register(GZipEncoder.class) .register(JacksonFeature.class) .register(new LoggingFilter(Logger.getLogger(BiocodePlugin.class.getName()), true)); @@ -74,7 +97,9 @@ void login(String username, String password) throws IOException, ProcessingExcep String clientSecret = secrets.get("client_secret").asText(); WebTarget path = target.path("/v1/oauth/accessToken"); - Invocation.Builder request = path.request(); + + Invocation.Builder request = path.request().accept(MediaType.APPLICATION_JSON); + Form formToPost = new Form() .param("username", username) .param("password", password) @@ -161,7 +186,7 @@ List getProjects(boolean includePublic) throws DatabaseServiceException Invocation.Builder request = target.path("projects") .queryParam("includePublic", includePublic) .request(MediaType.APPLICATION_JSON_TYPE); - + try { Response response = request.get(); List fromService = getRestServiceResult(new GenericType>() { diff --git a/src/com/biomatters/plugins/biocode/labbench/fims/geome/geomeFIMSConnection.java b/src/com/biomatters/plugins/biocode/labbench/fims/geome/geomeFIMSConnection.java index dd72a50ab..e8e94149b 100644 --- a/src/com/biomatters/plugins/biocode/labbench/fims/geome/geomeFIMSConnection.java +++ b/src/com/biomatters/plugins/biocode/labbench/fims/geome/geomeFIMSConnection.java @@ -18,9 +18,8 @@ import javax.ws.rs.client.Entity; import javax.ws.rs.client.Invocation; -import javax.ws.rs.core.Form; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; +import javax.ws.rs.client.WebTarget; +import javax.ws.rs.core.*; import java.lang.ref.SoftReference; import java.util.*; import java.util.function.BiConsumer; @@ -43,7 +42,7 @@ public String getName() { @Override public String getDescription() { - return "Connection to GeOMe (https://geome-db.org/)"; + return "Connection to GeOMe (FIMS3) at https://geome-db.org/"; } @Override @@ -94,9 +93,7 @@ public void _connect(Options options) throws ConnectionException { } } catch (Exception e) { - - e.printStackTrace(); - throw new ConnectionException("Unable to connect to GeOMe: " + e.getMessage()); + throw new ConnectionException("Unable to connect to GeOMe: " + e.getStackTrace()); } } @@ -242,9 +239,11 @@ private Project getProjectFromQuery(Query query) throws ConnectionException { if (query instanceof CompoundSearchQuery) { - if (((CompoundSearchQuery) query).getOperator() != CompoundSearchQuery.Operator.AND) { - throw new ConnectionException("OR queries with Project unsupported"); - } + // JBD: removing the restriction on OR queries from Geome... these DO work. + // However, i'm not certain why this restriction was placed here in the first place + //if (((CompoundSearchQuery) query).getOperator() != CompoundSearchQuery.Operator.AND) { + // throw new ConnectionException("OR queries with Project unsupported"); + //} for (Query childQuery : ((CompoundSearchQuery) query).getChildren()) { if (childQuery instanceof AdvancedSearchQueryTerm) { Project project = getProjectFromSearchTerm((AdvancedSearchQueryTerm) childQuery); @@ -392,68 +391,44 @@ private Map> mapResults(String idField, List _retrieveSamplesForTissueIds(List tissueIds, RetrieveCallback callback) throws ConnectionException { try { - /* - return tissueIds.stream() - .map(id -> sampleCache.get(id)) - .filter(s -> s != null) - .map(s -> { - if (callback != null) { - callback.add((PluginDocument) s, Collections.emptyMap()); - } - return s.get(); - }) - .collect(Collectors.toList()); - */ - List projectIds = new ArrayList<>(); - // collect project ids - for (Project currentProject : projects) { - projectIds.add(currentProject.id); - } - for (Project currentProject : projects) { - - Query[] tissueQueries = new Query[tissueIds.size()]; - for (int i = 0; i < tissueIds.size(); i++) { - tissueQueries[i] = Query.Factory.createFieldQuery(getTissueSampleDocumentField(), Condition.EQUAL, tissueIds.get(i)); + // Strip out empty tissue IDs -- they will make queries to Geome fail parser check + ArrayList tissueIdsArrayList = new ArrayList(); + for (int i = 0; i < tissueIds.size(); i++) { + if (!tissueIds.get(i).trim().equals("") && !(tissueIds.get(i) == null)) { + tissueIdsArrayList.add(tissueIds.get(i)); } - Query tissueQuery = Query.Factory.createOrQuery(tissueQueries, Collections.emptyMap()); - - String queryString = buildQuery(tissueQuery); - - - // POST Connection method... should work for large requests - Invocation.Builder searchRequest = client.getQueryTarget().path("records/Tissue/json") - .queryParam("_projects_:", projectIds) - .queryParam("entity", "Tissue") - .queryParam("limit", 100000) - .request(); - - Form formToPost = new Form() - .param("query", queryString + "_select_:[Tissue,Sample,Event]"); - - Response response = searchRequest.post( - Entity.entity(formToPost, MediaType.APPLICATION_FORM_URLENCODED_TYPE)); - /* - // GET Connection method... fails on large requests - Invocation.Builder searchRequest = client.getQueryTarget().path("records/Tissue/json") - .queryParam("_projects_:", projectIds) - .queryParam("entity", "Tissue") - .queryParam("limit", 100000) - .queryParam("q", queryString + "_select_:[Tissue,Sample,Event]") - .request(); - - Response response = searchRequest.get(); - */ - SearchResult result = geomeFIMSClient.getRestServiceResult(SearchResult.class, response); - - List samples = transformQueryResults(tissueIds, result); - return samples; } + Object[] trimmedTissueIds = tissueIdsArrayList.toArray(); + + // Build Tissue query + Query[] tissueQueries = new Query[trimmedTissueIds.length]; + for (int i = 0; i < trimmedTissueIds.length; i++) { + tissueQueries[i] = Query.Factory.createFieldQuery(getTissueSampleDocumentField(), Condition.EQUAL, trimmedTissueIds[i].toString()); + } + Query tissueQuery = Query.Factory.createOrQuery(tissueQueries, Collections.emptyMap()); + String tissueIDsToQuery = buildQuery(tissueQuery); + String queryString = tissueIDsToQuery + " _select_:[Tissue,Sample,Event]"; + + Invocation.Builder searchRequest = client.getQueryTarget().path("records/Tissue/json") + .queryParam("limit", 100000) + .request(); + + Form formToPost = new Form() + .param("query", queryString) + .param("entity", "Tissue"); + + Response response = searchRequest.post( + Entity.entity(formToPost, MediaType.APPLICATION_FORM_URLENCODED_TYPE)); + + SearchResult result = geomeFIMSClient.getRestServiceResult(SearchResult.class, response); + + List samples = transformQueryResults(tissueIds, result); + return samples; } catch (DatabaseServiceException e) { throw new ConnectionException(e); } - return null; } private List transformQueryResults(List tissueIds, SearchResult result) throws ConnectionException { @@ -663,7 +638,7 @@ private String getGenbankCountryValue(String country, String locality) { // remove the country name String tempLocalityField = locality.trim().replaceFirst(country.trim(), ""); // remove colons since they will be confusing in this context - tempLocalityField = tempLocalityField.replace(":",""); + tempLocalityField = tempLocalityField.replace(":", ""); genbankCountryValue += ":" + tempLocalityField; } else { genbankCountryValue += ":" + locality.trim(); diff --git a/src/com/biomatters/plugins/biocode/labbench/lims/LIMSConnection.java b/src/com/biomatters/plugins/biocode/labbench/lims/LIMSConnection.java index 123c6b054..cea502b8a 100644 --- a/src/com/biomatters/plugins/biocode/labbench/lims/LIMSConnection.java +++ b/src/com/biomatters/plugins/biocode/labbench/lims/LIMSConnection.java @@ -132,7 +132,7 @@ protected void iterateBatch(List batch) throws DatabaseServiceException public enum AvailableLimsTypes { local(LocalLIMSConnection.class, "Built-in MySQL Database", "Create and connect to LIMS databases on your local computer (stored with your Geneious data)"), remote(MysqlLIMSConnection.class, "Remote MySQL Database", "Connect to a LIMS database on a remote MySQL server"), - server(ServerLimsConnection.class, "Biocode Server", "Connect to an instance of the Biocode Server."); + server(ServerLimsConnection.class, "Biocode Server", "Connect to an instance of the Biocode Server (Beta)"); private final Class limsClass; private final String label; private final String description; diff --git a/src/com/biomatters/plugins/biocode/labbench/lims/SqlLimsConnection.java b/src/com/biomatters/plugins/biocode/labbench/lims/SqlLimsConnection.java index 00e09a54c..e3948f4b3 100644 --- a/src/com/biomatters/plugins/biocode/labbench/lims/SqlLimsConnection.java +++ b/src/com/biomatters/plugins/biocode/labbench/lims/SqlLimsConnection.java @@ -23,9 +23,11 @@ import jebl.util.ProgressListener; import javax.sql.DataSource; +import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; import java.lang.reflect.InvocationTargetException; +import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; import java.sql.*; @@ -39,8 +41,8 @@ * An SQL based {@link LIMSConnection} * * @author Matthew Cheung - *

- * Created on 1/04/14 4:45 PM + *

+ * Created on 1/04/14 4:45 PM */ public abstract class SqlLimsConnection extends LIMSConnection { @@ -59,7 +61,7 @@ public abstract class SqlLimsConnection extends LIMSConnection { private DataSource dataSource; - public synchronized DataSource getDataSource() throws SQLException{ + public synchronized DataSource getDataSource() throws SQLException { if (dataSource == null) throw new SQLException("LIMS database is disconnected, please try to relogin."); @@ -91,9 +93,10 @@ protected void _connect(PasswordOptions options) throws ConnectionException { } private Connection legacyConnection; + @Override protected synchronized Connection getConnectionInternal() throws SQLException { - if(legacyConnection == null) { + if (legacyConnection == null) { // By pass the new way of getting connections. Get one directly from the pool. legacyConnection = getDataSource().getConnection(); } @@ -106,7 +109,7 @@ protected synchronized Connection getConnectionInternal() throws SQLException { /** * @return A {@link com.biomatters.plugins.biocode.labbench.lims.SqlLimsConnection.ConnectionWrapper} from the * connection pool. Should be returned after use with {@link #returnConnection(com.biomatters.plugins.biocode.labbench.lims.SqlLimsConnection.ConnectionWrapper)} - * + * * @throws SQLException if the connection could not be established */ protected ConnectionWrapper getConnection() throws SQLException { @@ -117,7 +120,7 @@ protected ConnectionWrapper getConnection() throws SQLException { } synchronized (connectionCounts) { Integer current = connectionCounts.get(toReturn); - if(current == null) { + if (current == null) { current = 1; } else { current = current + 1; @@ -126,12 +129,13 @@ protected ConnectionWrapper getConnection() throws SQLException { } return toReturn; } - + private final Map connectionCounts = new HashMap(); + protected void returnConnection(ConnectionWrapper connection) { synchronized (connectionCounts) { Integer current = connectionCounts.get(connection); - if(current == null) { + if (current == null) { ConnectionWrapper.closeConnection(connection); } else { current = current - 1; @@ -155,7 +159,7 @@ public synchronized void disconnect() { //we used to explicitly close the SQL connection, but this was causing crashes if the user logged out while a query was in progress. //now we remove all references to it and let the garbage collector close it when the queries have finished. - if(legacyConnection != null) { + if (legacyConnection != null) { legacyConnection = null; } dataSource = null; @@ -206,13 +210,13 @@ private void performBackgroundInitializationTasks() { String lastStartedString = getProperty(BACKGROUND_TASKS_STARTED_KEY); long lastStarted; try { - lastStarted= lastStartedString == null || lastStartedString.trim().length() == 0 ? + lastStarted = lastStartedString == null || lastStartedString.trim().length() == 0 ? 0 : Long.parseLong(lastStartedString); } catch (NumberFormatException e) { lastStarted = 0; } int oneDayInMilliseconds = 24 * 60 * 60 * 1000; - if(lastStarted > System.currentTimeMillis() - oneDayInMilliseconds) { + if (lastStarted > System.currentTimeMillis() - oneDayInMilliseconds) { return; } setProperty(BACKGROUND_TASKS_STARTED_KEY, String.valueOf(System.currentTimeMillis())); @@ -242,7 +246,7 @@ private void handleBackgroundTaskFailure(Exception e) { try { String numFailString = getProperty(NUM_BACKGROUND_TASK_FAILS); int numFails = 0; - if(numFailString != null && numFailString.trim().length() > 0) { + if (numFailString != null && numFailString.trim().length() > 0) { try { numFails = Integer.parseInt(numFailString); } catch (NumberFormatException e1) { @@ -253,10 +257,10 @@ private void handleBackgroundTaskFailure(Exception e) { numFails++; setProperty(NUM_BACKGROUND_TASK_FAILS, String.valueOf(numFails)); - if(numFails > 10) { + if (numFails > 10) { BiocodeUtilities.displayExceptionDialog("Biocode Background Maintenance Tasks Failed", "The Biocode plugin has failed to perform background maintenance tasks the last " + numFails + - " times it has tried. Please contact " + BiocodePlugin.SUPPORT_EMAIL + " with these error details.", e, null); + " times it has tried. Please contact " + BiocodePlugin.SUPPORT_EMAIL + " with these error details.", e, null); } } catch (DatabaseServiceException e1) { // Can't do much in this case since we're trying to handle an error. Just print out exception to stderr @@ -270,7 +274,7 @@ private static void findAndAddMatchingReactions(Multimap ReactionDesc toFindPairOf = sameEditRecord.get(0); Collection reactions = workflowToReaction.get(toFindPairOf.workflow); for (ReactionDesc reaction : reactions) { - if(reaction.reactionId != toFindPairOf.reactionId && !reaction.direction.equals(toFindPairOf.direction)) { + if (reaction.reactionId != toFindPairOf.reactionId && !reaction.direction.equals(toFindPairOf.direction)) { sameEditRecord.add(reaction); } } @@ -340,18 +344,19 @@ private void populateFailureReasons(ConnectionWrapper connection) throws SQLExce /** *

- * The assembly table has a workflow column that may become inconsistent with the reaction the assembly belongs to. - * In the future schema update we should remove this column. + * The assembly table has a workflow column that may become inconsistent with the reaction the assembly belongs to. + * In the future schema update we should remove this column. *

*

- * We make sure to ignore this column when creating new sequences. However we we will still fix it up on start - * up so that any external systems that make use of the LIMS database directly don't get inconsistent data. + * We make sure to ignore this column when creating new sequences. However we we will still fix it up on start + * up so that any external systems that make use of the LIMS database directly don't get inconsistent data. *

*

- * This method can be removed once the column is removed in a schema update. + * This method can be removed once the column is removed in a schema update. *

* * @param connection + * * @throws SQLException */ private void makeAssemblyTablesWorkflowColumnConsistent(ConnectionWrapper connection) throws SQLException { @@ -380,12 +385,12 @@ private void makeAssemblyTablesWorkflowColumnConsistent(ConnectionWrapper connec } } inconsistentRowsSet.close(); - } catch(SQLException e) { - if(e instanceof BatchUpdateException) { + } catch (SQLException e) { + if (e instanceof BatchUpdateException) { BatchUpdateException batchException = (BatchUpdateException) e; - for(SQLException ex = batchException.getNextException(); ex != null; ex = ex.getNextException()) { + for (SQLException ex = batchException.getNextException(); ex != null; ex = ex.getNextException()) { String errorMessage = ex.getMessage(); - if(errorMessage != null && errorMessage.trim().length() > 0) { + if (errorMessage != null && errorMessage.trim().length() > 0) { System.out.println(errorMessage); } } @@ -409,7 +414,8 @@ public Dependency(String name, String version) { /** * Creates a {@link org.apache.commons.dbcp.BasicDataSource} using a custom ClassLoader. *

- * Note:This method is required because there is an older version of commons-dbcp in Geneious core class loader. This + * Note:This method is required because there is an older version of commons-dbcp in Geneious core class + * loader. This * means the class uses that class loader when looking for the JDBC driver class and cannot access the MySQL driver * bundled in the Biocode plugin. *

@@ -420,12 +426,14 @@ public Dependency(String name, String version) { * * @param connectionUrl The URL to connect to * @param username - *@param password @return A {@link javax.sql.DataSource} for the specified parameters. + * @param password @return A {@link javax.sql.DataSource} for the specified parameters. + * * @throws com.biomatters.plugins.biocode.labbench.ConnectionException */ public static DataSource createBasicDataSource(String connectionUrl, Driver driver, String username, String password) throws ConnectionException { + ClassLoader pluginClassLoader = SqlLimsConnection.class.getClassLoader(); - if(pluginClassLoader instanceof URLClassLoader) { + if (pluginClassLoader instanceof URLClassLoader) { URLClassLoader urlClassLoader = (URLClassLoader) pluginClassLoader; URL rootResource = urlClassLoader.getResource("."); System.out.println(rootResource); @@ -441,32 +449,33 @@ public static DataSource createBasicDataSource(String connectionUrl, Driver driv List urlsOfJar = new ArrayList(); for (URL url : (urlClassLoader).getURLs()) { for (Dependency toCheckAgainst : required) { - if(url.toString().contains(toCheckAgainst.name) && url.toString().contains(toCheckAgainst.version)) { + if (url.toString().contains(toCheckAgainst.name) && url.toString().contains(toCheckAgainst.version)) { urlsOfJar.add(url); } } } ClassLoader bootstrapClassLoader = ClassLoader.getSystemClassLoader().getParent(); - if(bootstrapClassLoader == null) { + if (bootstrapClassLoader == null) { throw new IllegalStateException("Expected system class loader to have a parent"); } // We specify the parent class loader of our new one as the bootstrap classloader so it wont' be able to load // Geneious core classes. URLClassLoader classLoaderForPluginLibsOnly = new URLClassLoader(urlsOfJar.toArray(new URL[1]), bootstrapClassLoader); + try { Class dataSourceClass = classLoaderForPluginLibsOnly.loadClass("org.apache.commons.dbcp.BasicDataSource"); - DataSource dataSource = (DataSource)dataSourceClass.newInstance(); + DataSource dataSource = (DataSource) dataSourceClass.newInstance(); // We have to use reflection here because we can't cast the class we created to the one loaded by Geneious core dataSourceClass.getDeclaredMethod("setDriverClassName", String.class).invoke(dataSource, driver.getClass().getName()); dataSourceClass.getDeclaredMethod("setUrl", String.class).invoke(dataSource, connectionUrl); - if(username != null) { + if (username != null) { dataSourceClass.getDeclaredMethod("setUsername", String.class).invoke(dataSource, username); } - if(password != null) { + if (password != null) { dataSourceClass.getDeclaredMethod("setPassword", String.class).invoke(dataSource, password); } - if(Geneious.isHeadless()) { + if (Geneious.isHeadless()) { dataSourceClass.getDeclaredMethod("setMaxActive", int.class).invoke(dataSource, 25); } return dataSource; @@ -496,6 +505,7 @@ public static DataSource createBasicDataSource(String connectionUrl, Driver driv * {@link #canUpgradeDatabase()} * * @param currentVersion + * * @throws SQLException if a database problem occurs during the upgrade */ protected void upgradeDatabase(String currentVersion) throws ConnectionException { @@ -523,10 +533,10 @@ private void updateFkTracesConstraintIfNecessary(DataSource dataSource, Progress connection = dataSource.getConnection(); String selectFkTracesConstraintQuery = "SELECT * " + - "FROM information_schema.referential_constraints " + - "WHERE table_name=? " + - "AND referenced_table_name=? " + - "AND constraint_schema IN (SELECT DATABASE())"; + "FROM information_schema.referential_constraints " + + "WHERE table_name=? " + + "AND referenced_table_name=? " + + "AND constraint_schema IN (SELECT DATABASE())"; selectFkTracesConstraintStatement = connection.prepareStatement(selectFkTracesConstraintQuery); @@ -535,7 +545,7 @@ private void updateFkTracesConstraintIfNecessary(DataSource dataSource, Progress selectFkTracesConstraintStatement.setObject(2, "cyclesequencing"); selectFkTracesContraintResult = selectFkTracesConstraintStatement.executeQuery(); - if (!selectFkTracesContraintResult.next()) { + if (!selectFkTracesContraintResult.next()) { System.err.println("Invalid database schema: missing constraint 'Fktraces' between the traces table and the cyclesequencing table."); return; } @@ -547,14 +557,14 @@ private void updateFkTracesConstraintIfNecessary(DataSource dataSource, Progress String constraintName = selectFkTracesContraintResult.getString("CONSTRAINT_NAME"); String dropExistingFkTracesConstraintQuery = "ALTER TABLE lims.traces " + - "DROP FOREIGN KEY " + constraintName; - + "DROP FOREIGN KEY " + constraintName; + String addNewFkTracesConstraintQuery = "ALTER TABLE lims.traces " + - "ADD CONSTRAINT " + constraintName + - " FOREIGN KEY (reaction)" + - " REFERENCES lims.cyclesequencing (id)" + - " ON UPDATE CASCADE" + - " ON DELETE CASCADE"; + "ADD CONSTRAINT " + constraintName + + " FOREIGN KEY (reaction)" + + " REFERENCES lims.cyclesequencing (id)" + + " ON UPDATE CASCADE" + + " ON DELETE CASCADE"; PreparedStatement getTraceCount = connection.prepareStatement("SELECT COUNT(id) FROM traces"); @@ -562,7 +572,7 @@ private void updateFkTracesConstraintIfNecessary(DataSource dataSource, Progress traceCountResultSet.next(); int numTraces = traceCountResultSet.getInt(1); - if(!Geneious.isHeadless()) { + if (!Geneious.isHeadless()) { int estimateInMinutes = numTraces / 1000; String estimate; if (estimateInMinutes < 5) { @@ -622,7 +632,7 @@ private void updateFkTracesConstraintIfNecessary(DataSource dataSource, Progress } System.out.println("Successfully updated database schema."); - } catch(SQLException e) { + } catch (SQLException e) { StringWriter stacktrace = new StringWriter(); e.printStackTrace(new PrintWriter(stacktrace)); @@ -655,6 +665,7 @@ private void updateFkTracesConstraintIfNecessary(DataSource dataSource, Progress /** * @return an error message or null if everything is OK + * * @throws ConnectionException */ private String verifyDatabaseVersionAndUpgradeIfNecessary(ConnectionWrapper connection) throws ConnectionException { @@ -738,7 +749,7 @@ public LimsSearchResult getMatchingDocumentsFromLims(Query query, Collection terms = new ArrayList(); CompoundSearchQuery.Operator operator; if (processedQuery instanceof CompoundSearchQuery) { - CompoundSearchQuery compoundQuery = (CompoundSearchQuery)processedQuery; + CompoundSearchQuery compoundQuery = (CompoundSearchQuery) processedQuery; operator = compoundQuery.getOperator(); for (Query innerQuery : compoundQuery.getChildren()) { if (isCompatibleSearchQueryTerm(innerQuery)) { @@ -747,7 +758,7 @@ public LimsSearchResult getMatchingDocumentsFromLims(Query query, Collection conditions = new ArrayList(); - if(plateQueryConditions != null) { + if (plateQueryConditions != null) { conditions.add("(" + plateQueryConditions + ")"); } - if(assemblyQueryConditions != null) { + if (assemblyQueryConditions != null) { conditions.add("(" + assemblyQueryConditions + ")"); } - if(!conditions.isEmpty()) { + if (!conditions.isEmpty()) { queryBuilder.append(" AND (").append(StringUtilities.join(operator.toString(), conditions)).append(")"); } @@ -908,12 +919,11 @@ private String getPcrAndSequencingPlatesWithNoWorkflowQuery(CompoundSearchQuery. } - private void setInitialTraceCountsForWorkflowDocuments(Collection workflows) throws SQLException { Map sequencingReactions = new HashMap(); for (WorkflowDocument workflowDocument : workflows) { for (Reaction reaction : workflowDocument.getReactions(Reaction.Type.CycleSequencing)) { - sequencingReactions.put(reaction.getId(), (CycleSequencingReaction)reaction); + sequencingReactions.put(reaction.getId(), (CycleSequencingReaction) reaction); } } setInitialTraceCountsForCycleSequencingReactions(sequencingReactions); @@ -932,7 +942,7 @@ private void setInitialTraceCountsForPlates(Map plateMap) throws } private void setInitialTraceCountsForCycleSequencingReactions(Map idToReactionMap) throws SQLException { - if(idToReactionMap.isEmpty()) { + if (idToReactionMap.isEmpty()) { return; } List cyclesequencingIds = new ArrayList(idToReactionMap.keySet()); @@ -969,12 +979,15 @@ private void setInitialTraceCountsForCycleSequencingReactions(Map tissueIdsToMatch, CompoundSearchQuery.Operator operator, QueryPart workflowQueryConditions, QueryPart extractionQueryConditions, @@ -994,19 +1007,19 @@ private StringBuilder constructLimsQueryString(Collection tissueIdsToMat queryBuilder.append("(SELECT * FROM extraction WHERE "); } - if(filterOnTissues) { + if (filterOnTissues) { String sampleColumn = isLocal() ? "LOWER(sampleId)" : "sampleId"; // MySQL is case insensitive by default conditionBuilder.append(sampleColumn).append(" IN "); SqlUtilities.appendSetOfQuestionMarks(conditionBuilder, tissueIdsToMatch.size()); } - if(filterOnTissues && extractionQueryConditions != null) { + if (filterOnTissues && extractionQueryConditions != null) { conditionBuilder.append(operatorString); } - if(extractionQueryConditions != null) { + if (extractionQueryConditions != null) { conditionBuilder.append("(").append(extractionQueryConditions).append(")"); } - if(operator == CompoundSearchQuery.Operator.AND) { + if (operator == CompoundSearchQuery.Operator.AND) { queryBuilder.append(")"); } queryBuilder.append(" extraction "); @@ -1025,7 +1038,7 @@ private StringBuilder constructLimsQueryString(Collection tissueIdsToMat } queryBuilder.append(" LEFT OUTER JOIN ").append(GelQuantificationReaction.DB_TABLE_NAME).append(" ON ") - .append(GelQuantificationReaction.DB_TABLE_NAME).append(".extractionId = extraction.id"); + .append(GelQuantificationReaction.DB_TABLE_NAME).append(".extractionId = extraction.id"); queryBuilder.append(" LEFT OUTER JOIN ").append("pcr ON pcr.workflow = workflow.id "); queryBuilder.append(" LEFT OUTER JOIN ").append("cyclesequencing ON cyclesequencing.workflow = workflow.id "); @@ -1159,7 +1172,7 @@ public Map> getGelImages(Collection plateIds) t } public Set getAllExtractionIdsForTissueIds_(List tissueIds) throws DatabaseServiceException { - if(tissueIds.isEmpty()) { + if (tissueIds.isEmpty()) { return Collections.emptySet(); } List queries = new ArrayList(); @@ -1233,11 +1246,11 @@ public List getExtractionsFromBarcodes_(List barcode } public Collection getPlatesUsingCocktail(Reaction.Type type, int cocktailId) throws DatabaseServiceException { - if(cocktailId < 0) { + if (cocktailId < 0) { return Collections.emptyList(); } String tableName; - switch(type) { + switch (type) { case PCR: tableName = "pcr"; break; @@ -1245,16 +1258,16 @@ public Collection getPlatesUsingCocktail(Reaction.Type type, int cocktai tableName = "cyclesequencing"; break; default: - throw new RuntimeException(type+" reactions cannot have a cocktail"); + throw new RuntimeException(type + " reactions cannot have a cocktail"); } - String sql = "SELECT plate.name FROM plate, "+tableName+" WHERE "+tableName+".plate = plate.id AND "+tableName+".cocktail = "+cocktailId; + String sql = "SELECT plate.name FROM plate, " + tableName + " WHERE " + tableName + ".plate = plate.id AND " + tableName + ".cocktail = " + cocktailId; ConnectionWrapper connection = null; try { connection = getConnection(); ResultSet resultSet = connection.executeQuery(sql); Set plateNames = new LinkedHashSet(); - while(resultSet.next()) { + while (resultSet.next()) { plateNames.add(resultSet.getString(1)); } return plateNames; @@ -1266,17 +1279,17 @@ public Collection getPlatesUsingCocktail(Reaction.Type type, int cocktai } public List getPlatesUsingThermocycle(int thermocycleId) throws DatabaseServiceException { - if(thermocycleId < 0) { + if (thermocycleId < 0) { return Collections.emptyList(); } - String sql = "SELECT name FROM plate WHERE thermocycle = "+thermocycleId; + String sql = "SELECT name FROM plate WHERE thermocycle = " + thermocycleId; ConnectionWrapper connection = null; try { connection = getConnection(); ResultSet resultSet = connection.executeQuery(sql); List plateNames = new ArrayList(); - while(resultSet.next()) { + while (resultSet.next()) { plateNames.add(resultSet.getString(1)); } return plateNames; @@ -1330,11 +1343,11 @@ public Set deletePlates(List plates, ProgressListener progress) return plateIds; } -private void deleteReactions(ProgressListener progress, Plate plate) throws DatabaseServiceException { + private void deleteReactions(ProgressListener progress, Plate plate) throws DatabaseServiceException { progress.setMessage("deleting reactions"); String tableName; - switch(plate.getReactionType()) { + switch (plate.getReactionType()) { case Extraction: tableName = "extraction"; break; @@ -1351,8 +1364,8 @@ private void deleteReactions(ProgressListener progress, Plate plate) throws Data } ArrayList terms = new ArrayList(); - for(Reaction r : plate.getReactions()) { - if(r.getId() >= 0) { + for (Reaction r : plate.getReactions()) { + if (r.getId() >= 0) { terms.add(r.getId()); } } @@ -1360,7 +1373,7 @@ private void deleteReactions(ProgressListener progress, Plate plate) throws Data deleteRecords(tableName, "id", terms); } - public void renamePlate(int id, String newName) throws DatabaseServiceException{ + public void renamePlate(int id, String newName) throws DatabaseServiceException { ConnectionWrapper connection = null; try { connection = getConnection(); @@ -1431,6 +1444,7 @@ private boolean isCompatibleSearchQueryTerm(Query query) { * then it will not be included in the results. * * @param terms A list of terms to filter + * * @return A mapping from LIMS table to query term */ Map> mapQueryTermsToTable(List terms) { @@ -1475,8 +1489,8 @@ public List getWorkflowsById_(Collection workflowIds, ResultSet workflowsSet = selectWorkflow.executeQuery(); System.out.println("\tTook " + (System.currentTimeMillis() - start) + "ms to do LIMS (workflows) query"); - while(workflowsSet.next()) { - if(cancelable.isCanceled()) { + while (workflowsSet.next()) { + if (cancelable.isCanceled()) { return Collections.emptyList(); } int workflowId = workflowsSet.getInt("workflow.id"); @@ -1496,7 +1510,7 @@ public List getWorkflowsById_(Collection workflowIds, workflowsSet.close(); setInitialTraceCountsForWorkflowDocuments(byId.values()); } catch (SQLException e) { - throw new DatabaseServiceException(e, "Failed to retrieve workflow documents: " + e.getMessage(),false); + throw new DatabaseServiceException(e, "Failed to retrieve workflow documents: " + e.getMessage(), false); } finally { SqlUtilities.cleanUpStatements(selectWorkflow); returnConnection(connection); @@ -1525,7 +1539,7 @@ public List getWorkflowsById_(Collection workflowIds, } public List getPlates_(Collection plateIds, Cancelable cancelable) throws DatabaseServiceException { - if(plateIds.isEmpty()) { + if (plateIds.isEmpty()) { return Collections.emptyList(); } ConnectionWrapper connection = null; @@ -1585,7 +1599,7 @@ private List getPlatesFromResultSet(ResultSet resultSet, Cancelable cance int previousId = -1; while (resultSet.next()) { - if(cancelable.isCanceled()) { + if (cancelable.isCanceled()) { return Collections.emptyList(); } Plate plate; @@ -1657,6 +1671,7 @@ public void run() { * * @param key The name of the property * @param value The value to set for the property + * * @throws SQLException if something goes wrong communicating with the database. */ public void setProperty(String key, String value) throws DatabaseServiceException { @@ -1686,10 +1701,13 @@ public void setProperty(String key, String value) throws DatabaseServiceExceptio } /** - * Retrieves a property from the database previously set by calling {@link LIMSConnection#setProperty(String, String)} + * Retrieves a property from the database previously set by calling {@link LIMSConnection#setProperty(String, + * String)} * * @param key The name of the property to retrieve + * * @return value of the property or null if it does not exist + * * @throws SQLException if something goes wrong communicating with the database. */ public String getProperty(String key) throws DatabaseServiceException { @@ -1723,9 +1741,9 @@ private List addWorkflows(List reactions, ProgressListener p PreparedStatement statement = connection.prepareStatement("INSERT INTO workflow(locus, extractionId, date) VALUES (?, (SELECT extraction.id from extraction where extraction.extractionId = ?), ?)"); PreparedStatement statement2 = isLocal() ? connection.prepareStatement("CALL IDENTITY();") : connection.prepareStatement("SELECT last_insert_id()"); PreparedStatement statement3 = connection.prepareStatement("UPDATE workflow SET name = ? WHERE id=?"); - for(int i=0; i < reactions.size(); i++) { - if(progress != null) { - progress.setMessage("Creating new workflow "+(i+1)+" of "+reactions.size()); + for (int i = 0; i < reactions.size(); i++) { + if (progress != null) { + progress.setMessage("Creating new workflow " + (i + 1) + " of " + reactions.size()); } statement.setString(2, reactions.get(i).getExtractionId()); statement.setString(1, reactions.get(i).getLocus()); @@ -1734,8 +1752,8 @@ private List addWorkflows(List reactions, ProgressListener p ResultSet resultSet = statement2.executeQuery(); resultSet.next(); int workflowId = resultSet.getInt(1); - workflows.add(new Workflow(workflowId, "workflow"+workflowId, reactions.get(i).getExtractionId(), reactions.get(i).getLocus(), new Date())); - statement3.setString(1, reactions.get(i).getLocus()+"_workflow"+workflowId); + workflows.add(new Workflow(workflowId, "workflow" + workflowId, reactions.get(i).getExtractionId(), reactions.get(i).getLocus(), new Date())); + statement3.setString(1, reactions.get(i).getLocus() + "_workflow" + workflowId); statement3.setInt(2, workflowId); statement3.execute(); } @@ -1745,7 +1763,7 @@ private List addWorkflows(List reactions, ProgressListener p statement3.close(); connection.endTransaction(); return workflows; - } catch(SQLException e) { + } catch (SQLException e) { throw new DatabaseServiceException(e, e.getMessage(), false); } finally { returnConnection(connection); @@ -1815,7 +1833,7 @@ private Set deleteWorkflows(ProgressListener progress, Plate plate) thr @Override public List getWorkflowsByName(Collection workflowNames) throws DatabaseServiceException { - if(workflowNames.isEmpty()) { + if (workflowNames.isEmpty()) { return Collections.emptyList(); } ConnectionWrapper connection = null; @@ -1846,7 +1864,7 @@ public List getWorkflowsByName(Collection workflowNames) throw } statement.close(); return result; - } catch(SQLException e) { + } catch (SQLException e) { throw new DatabaseServiceException(e, e.getMessage(), false); } finally { returnConnection(connection); @@ -1857,7 +1875,7 @@ public List getWorkflowsByName(Collection workflowNames) throw public Map getWorkflowIds(List idsToCheck, List loci, Reaction.Type reactionType) throws DatabaseServiceException { StringBuilder sqlBuilder = new StringBuilder(); List values = new ArrayList(); - switch(reactionType) { + switch (reactionType) { case Extraction: throw new RuntimeException("You should not be adding extractions to existing workflows!"); case GelQuantification: @@ -1866,17 +1884,16 @@ public Map getWorkflowIds(List idsToCheck, List case CycleSequencing: sqlBuilder.append("SELECT extraction.extractionId AS id, workflow.name AS workflow, workflow.date AS date, workflow.id AS workflowId, extraction.date FROM extraction, workflow WHERE workflow.extractionId = extraction.id AND ("); for (int i = 0; i < idsToCheck.size(); i++) { - if(loci.get(i) != null && loci.get(i).length() > 0) { + if (loci.get(i) != null && loci.get(i).length() > 0) { sqlBuilder.append("(extraction.extractionId = ? AND locus = ?)"); values.add(idsToCheck.get(i)); values.add(loci.get(i)); - } - else { + } else { sqlBuilder.append("extraction.extractionId = ?"); values.add(idsToCheck.get(i)); } - if(i < idsToCheck.size()-1) { + if (i < idsToCheck.size() - 1) { sqlBuilder.append(" OR "); } } @@ -2066,7 +2083,7 @@ private AssembledSequence getAssembledSequence(ResultSet resultSet) throws SQLEx seq.reversePrimerName = resultSet.getString("revPrName"); seq.reversePrimerSequence = resultSet.getString("revPrSequence"); java.sql.Date created = resultSet.getDate("date"); - if(created != null) { + if (created != null) { seq.date = created.getTime(); } seq.forwardPlate = resultSet.getString("forwardPlate"); @@ -2153,7 +2170,7 @@ private void appendValue(List inserts, StringBuilder sql, boolean append */ if (Date.class.isAssignableFrom(value.getClass())) { GregorianCalendar date = new GregorianCalendar(); - date.setTime((Date)value); + date.setTime((Date) value); switch (condition) { case DATE_BEFORE: case DATE_AFTER_OR_ON: @@ -2172,7 +2189,7 @@ private void appendValue(List inserts, StringBuilder sql, boolean append default: break; } - value = (Object)date.getTime(); + value = (Object) date.getTime(); } if (value instanceof String) { inserts.add(termSurrounder.getPrepend() + valueString + termSurrounder.getAppend()); @@ -2227,7 +2244,7 @@ public void createOrUpdatePlate(Plate plate, ProgressListener progress) throws D PreparedStatement statement = plateToSQL(connection, plate); statement.execute(); statement.close(); - if(plate.getId() < 0) { + if (plate.getId() < 0) { PreparedStatement statement1 = isLocal() ? connection.prepareStatement("CALL IDENTITY();") : connection.prepareStatement("SELECT last_insert_id()"); ResultSet resultSet = statement1.executeQuery(); resultSet.next(); @@ -2237,13 +2254,13 @@ public void createOrUpdatePlate(Plate plate, ProgressListener progress) throws D } //replace the images - if(plate.gelImagesHaveBeenDownloaded()) { //don't modify the gel images if we haven't downloaded them from the server or looked at them... - if(!BiocodeService.getInstance().deleteAllowed("gelimages")) { + if (plate.gelImagesHaveBeenDownloaded()) { //don't modify the gel images if we haven't downloaded them from the server or looked at them... + if (!BiocodeService.getInstance().deleteAllowed("gelimages")) { throw new SQLException("It appears that you do not have permission to delete GEL Images. Please contact your System Administrator for assistance"); } PreparedStatement deleteImagesStatement = connection.prepareStatement("DELETE FROM gelimages WHERE plate=" + plate.getId()); deleteImagesStatement.execute(); - for(GelImage image : plate.getImages()) { + for (GelImage image : plate.getImages()) { PreparedStatement statement1 = gelToSql(connection, image); statement1.execute(); statement1.close(); @@ -2252,7 +2269,7 @@ public void createOrUpdatePlate(Plate plate, ProgressListener progress) throws D } saveReactions(plate.getReactions(), plate.getReactionType(), progress); - if(plate.getReactionType() != Reaction.Type.GelQuantification) { + if (plate.getReactionType() != Reaction.Type.GelQuantification) { updateWorkflows(connection, plate); } @@ -2301,7 +2318,7 @@ private void updateWorkflows(ConnectionWrapper connection, Plate plate) throws S } //update the last-modified on the workflows associated with this plate... - if(plate.getReactionType() != Reaction.Type.GelQuantification) { + if (plate.getReactionType() != Reaction.Type.GelQuantification) { String sql; if (plate.getReactionType() == Reaction.Type.Extraction) { sql = "UPDATE workflow SET workflow.date = (SELECT date from plate WHERE plate.id=" + plate.getId() + ") WHERE extractionId IN (SELECT id FROM extraction WHERE extraction.plate=" + plate.getId() + ")"; @@ -2327,17 +2344,16 @@ private static PreparedStatement gelToSql(ConnectionWrapper connection, GelImage return statement; } - private static PreparedStatement plateToSQL(ConnectionWrapper connection, Plate plate) throws SQLException{ + private static PreparedStatement plateToSQL(ConnectionWrapper connection, Plate plate) throws SQLException { String name = plate.getName(); - if(name == null || name.trim().length() == 0) { + if (name == null || name.trim().length() == 0) { throw new SQLException("Plates cannot have empty names"); } PreparedStatement statement; int id = plate.getId(); - if(id < 0) { + if (id < 0) { statement = connection.prepareStatement("INSERT INTO plate (name, size, type, thermocycle, date) VALUES (?, ?, ?, ?, ?)"); - } - else { + } else { statement = connection.prepareStatement("UPDATE plate SET name=?, size=?, type=?, thermocycle=?, date=? WHERE id=?"); statement.setInt(6, id); } @@ -2345,10 +2361,9 @@ private static PreparedStatement plateToSQL(ConnectionWrapper connection, Plate statement.setInt(2, plate.getReactions().length); statement.setString(3, plate.getReactionType().toString()); Thermocycle tc = plate.getThermocycle(); - if(tc != null) { + if (tc != null) { statement.setInt(4, tc.getId()); - } - else { + } else { statement.setInt(4, plate.getThermocycleId()); } statement.setDate(5, new java.sql.Date(new Date().getTime())); @@ -2368,7 +2383,7 @@ public void isPlateValid(Plate plate) throws DatabaseServiceException { } plateCheckStatement.close(); } - if (plate.getThermocycle() == null && plate.getReactionType().hasThermocycles()) { + if (plate.getThermocycle() == null && plate.getReactionType().hasThermocycles()) { throw new BadDataException("The plate has no thermocycle set"); } } catch (BadDataException e) { @@ -2379,7 +2394,7 @@ public void isPlateValid(Plate plate) throws DatabaseServiceException { } public List getEmptyPlates(Collection plateIds) throws DatabaseServiceException { - if(plateIds == null || plateIds.size() == 0) { + if (plateIds == null || plateIds.size() == 0) { return Collections.emptyList(); } @@ -2388,13 +2403,13 @@ public List getEmptyPlates(Collection plateIds) throws DatabaseS List idMatches = new ArrayList(); - for(Integer num : plateIds) { - idMatches.add("id="+num); + for (Integer num : plateIds) { + idMatches.add("id=" + num); } String termString = StringUtilities.join(" OR ", idMatches); - if(termString.length() > 0) { - sql += " AND ("+termString+")"; + if (termString.length() > 0) { + sql += " AND (" + termString + ")"; } ConnectionWrapper connection = null; @@ -2402,7 +2417,7 @@ public List getEmptyPlates(Collection plateIds) throws DatabaseS connection = getConnection(); ResultSet resultSet = connection.executeQuery(sql); List result = new ArrayList(); - while(resultSet.next()) { + while (resultSet.next()) { Plate plate = new Plate(resultSet); plate.initialiseReactions(); result.add(plate); @@ -2479,28 +2494,27 @@ public void saveReactions(Reaction[] reactions, Reaction.Type type, ProgressList connection = getConnection(); PreparedStatement getLastId = BiocodeService.getInstance().getActiveLIMSConnection().isLocal() ? connection.prepareStatement("CALL IDENTITY();") : connection.prepareStatement("SELECT last_insert_id()"); - switch(type) { + switch (type) { case Extraction: String insertSQL; String updateSQL; - insertSQL = "INSERT INTO extraction (method, volume, dilution, parent, sampleId, extractionId, extractionBarcode, plate, location, notes, previousPlate, previousWell, date, technician, concentrationStored, concentration, gelimage, control) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; - updateSQL = "UPDATE extraction SET method=?, volume=?, dilution=?, parent=?, sampleId=?, extractionId=?, extractionBarcode=?, plate=?, location=?, notes=?, previousPlate=?, previousWell=?, date=?, technician=?, concentrationStored=?, concentration=?, gelImage=?, control=? WHERE id=?"; + insertSQL = "INSERT INTO extraction (method, volume, dilution, parent, sampleId, extractionId, extractionBarcode, plate, location, notes, previousPlate, previousWell, date, technician, concentrationStored, concentration, gelimage, control) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; + updateSQL = "UPDATE extraction SET method=?, volume=?, dilution=?, parent=?, sampleId=?, extractionId=?, extractionBarcode=?, plate=?, location=?, notes=?, previousPlate=?, previousWell=?, date=?, technician=?, concentrationStored=?, concentration=?, gelImage=?, control=? WHERE id=?"; PreparedStatement insertStatement = connection.prepareStatement(insertSQL); PreparedStatement updateStatement = connection.prepareStatement(updateSQL); for (int i = 0; i < reactions.length; i++) { Reaction reaction = reactions[i]; - if(progress != null) { - progress.setMessage("Saving reaction "+(i+1)+" of "+reactions.length); + if (progress != null) { + progress.setMessage("Saving reaction " + (i + 1) + " of " + reactions.length); } if (!reaction.isEmpty() && reaction.getPlateId() >= 0) { PreparedStatement statement; boolean isUpdateNotInsert = reaction.getId() >= 0; - if(isUpdateNotInsert) { //the reaction is already in the database + if (isUpdateNotInsert) { //the reaction is already in the database statement = updateStatement; statement.setInt(19, reaction.getId()); - } - else { + } else { statement = insertStatement; } ReactionOptions options = reaction.getOptions(); @@ -2516,20 +2530,20 @@ public void saveReactions(Reaction[] reactions, Reaction.Type type, ProgressList statement.setString(10, options.getValueAsString("notes")); statement.setString(11, options.getValueAsString("previousPlate")); statement.setString(12, options.getValueAsString("previousWell")); - statement.setDate(13, new java.sql.Date(((Date)options.getValue("date")).getTime())); + statement.setDate(13, new java.sql.Date(((Date) options.getValue("date")).getTime())); statement.setString(14, options.getValueAsString("technician")); statement.setInt(15, "yes".equals(options.getValueAsString("concentrationStored")) ? 1 : 0); - statement.setDouble(16, (Double)options.getValue("concentration")); + statement.setDouble(16, (Double) options.getValue("concentration")); GelImage image = reaction.getGelImage(); statement.setBytes(17, image != null ? image.getImageBytes() : null); statement.setString(18, options.getValueAsString("control")); - if(isUpdateNotInsert) { + if (isUpdateNotInsert) { updateStatement.executeUpdate(); } else { insertStatement.executeUpdate(); ResultSet resultSet = getLastId.executeQuery(); - if(resultSet.next()) { + if (resultSet.next()) { reaction.setId(resultSet.getInt(1)); } resultSet.close(); @@ -2547,63 +2561,59 @@ public void saveReactions(Reaction[] reactions, Reaction.Type type, ProgressList int saveCount = 0; for (int i = 0; i < reactions.length; i++) { Reaction reaction = reactions[i]; - if(progress != null) { - progress.setMessage("Saving reaction "+(i+1)+" of "+reactions.length); + if (progress != null) { + progress.setMessage("Saving reaction " + (i + 1) + " of " + reactions.length); } if (!reaction.isEmpty() && reaction.getPlateId() >= 0) { PreparedStatement statement; - if(reaction.getId() >= 0) { //the reaction is already in the database + if (reaction.getId() >= 0) { //the reaction is already in the database statement = updateStatement; statement.setInt(18, reaction.getId()); - } - else { + } else { statement = insertStatement; } ReactionOptions options = reaction.getOptions(); Options.Option option = options.getOption(PCROptions.PRIMER_OPTION_ID); - if(!(option instanceof DocumentSelectionOption)) { - throw new SQLException("Could not save reactions - expected primer type "+DocumentSelectionOption.class.getCanonicalName()+" but found a "+option.getClass().getCanonicalName()); + if (!(option instanceof DocumentSelectionOption)) { + throw new SQLException("Could not save reactions - expected primer type " + DocumentSelectionOption.class.getCanonicalName() + " but found a " + option.getClass().getCanonicalName()); } - List primerOptionValue = ((DocumentSelectionOption)option).getDocuments(); - if(primerOptionValue.size() == 0) { + List primerOptionValue = ((DocumentSelectionOption) option).getDocuments(); + if (primerOptionValue.size() == 0) { statement.setString(1, "None"); statement.setString(2, ""); - } - else { + } else { AnnotatedPluginDocument selectedDoc = primerOptionValue.get(0); - NucleotideSequenceDocument sequence = (NucleotideSequenceDocument)selectedDoc.getDocumentOrThrow(SQLException.class); + NucleotideSequenceDocument sequence = (NucleotideSequenceDocument) selectedDoc.getDocumentOrThrow(SQLException.class); statement.setString(1, selectedDoc.getName()); statement.setString(2, sequence.getSequenceString()); } //statement.setInt(3, (Integer)options.getValue("prAmount")); Options.Option option2 = options.getOption(PCROptions.PRIMER_REVERSE_OPTION_ID); - if(!(option2 instanceof DocumentSelectionOption)) { - throw new SQLException("Could not save reactions - expected primer type "+DocumentSelectionOption.class.getCanonicalName()+" but found a "+option2.getClass().getCanonicalName()); + if (!(option2 instanceof DocumentSelectionOption)) { + throw new SQLException("Could not save reactions - expected primer type " + DocumentSelectionOption.class.getCanonicalName() + " but found a " + option2.getClass().getCanonicalName()); } - List primerOptionValue2 = ((DocumentSelectionOption)option2).getDocuments(); - if(primerOptionValue2.size() == 0) { + List primerOptionValue2 = ((DocumentSelectionOption) option2).getDocuments(); + if (primerOptionValue2.size() == 0) { statement.setString(13, "None"); statement.setString(14, ""); - } - else { + } else { AnnotatedPluginDocument selectedDoc = primerOptionValue2.get(0); - NucleotideSequenceDocument sequence = (NucleotideSequenceDocument)selectedDoc.getDocumentOrThrow(SQLException.class); + NucleotideSequenceDocument sequence = (NucleotideSequenceDocument) selectedDoc.getDocumentOrThrow(SQLException.class); statement.setString(13, selectedDoc.getName()); statement.setString(14, sequence.getSequenceString()); } - statement.setDate(15, new java.sql.Date(((Date)options.getValue("date")).getTime())); + statement.setDate(15, new java.sql.Date(((Date) options.getValue("date")).getTime())); statement.setString(16, options.getValueAsString("technician")); - // statement.setInt(14, (Integer)options.getValue("revPrAmount")); - // if (reaction.getWorkflow() == null || reaction.getWorkflow().getId() < 0) { - // throw new SQLException("The reaction " + reaction.getId() + " does not have a workflow set."); - // } + // statement.setInt(14, (Integer)options.getValue("revPrAmount")); + // if (reaction.getWorkflow() == null || reaction.getWorkflow().getId() < 0) { + // throw new SQLException("The reaction " + reaction.getId() + " does not have a workflow set."); + // } //statement.setInt(4, reaction.getWorkflow() != null ? reaction.getWorkflow().getId() : 0); - if(reaction.getWorkflow() != null) { + if (reaction.getWorkflow() != null) { statement.setInt(3, reaction.getWorkflow().getId()); - } - else { + } else { statement.setObject(3, null); } statement.setInt(4, reaction.getPlateId()); @@ -2612,23 +2622,21 @@ public void saveReactions(Reaction[] reactions, Reaction.Type type, ProgressList Options.OptionValue cocktailValue = (Options.OptionValue) options.getValue("cocktail"); try { cocktailId = Integer.parseInt(cocktailValue.getName()); + } catch (NumberFormatException ex) { + throw new SQLException("The reaction " + reaction.getId() + " does not have a valid cocktail (" + cocktailValue.getLabel() + ", " + cocktailValue.getName() + ")."); } - catch(NumberFormatException ex) { - throw new SQLException("The reaction " + reaction.getId() + " does not have a valid cocktail ("+ cocktailValue.getLabel()+", "+cocktailValue.getName()+")."); - } - if(cocktailId < 0) { - throw new SQLException("The reaction " + reaction.getPosition() + " does not have a valid cocktail ("+cocktailValue.getName()+")."); + if (cocktailId < 0) { + throw new SQLException("The reaction " + reaction.getPosition() + " does not have a valid cocktail (" + cocktailValue.getName() + ")."); } statement.setInt(6, cocktailId); - statement.setString(7, ((Options.OptionValue)options.getValue(ReactionOptions.RUN_STATUS)).getLabel()); - if(reaction.getThermocycle() != null) { + statement.setString(7, ((Options.OptionValue) options.getValue(ReactionOptions.RUN_STATUS)).getLabel()); + if (reaction.getThermocycle() != null) { statement.setInt(8, reaction.getThermocycle().getId()); - } - else { + } else { statement.setInt(8, -1); } - statement.setInt(9, ((Options.OptionValue)options.getValue("cleanupPerformed")).getName().equals("true") ? 1 : 0); + statement.setInt(9, ((Options.OptionValue) options.getValue("cleanupPerformed")).getName().equals("true") ? 1 : 0); statement.setString(10, options.getValueAsString("cleanupMethod")); statement.setString(11, reaction.getExtractionId()); System.out.println(reaction.getExtractionId()); @@ -2637,9 +2645,9 @@ public void saveReactions(Reaction[] reactions, Reaction.Type type, ProgressList statement.setBytes(17, image != null ? image.getImageBytes() : null); statement.execute(); - if(reaction.getId() < 0) { + if (reaction.getId() < 0) { ResultSet resultSet = getLastId.executeQuery(); - if(resultSet.next()) { + if (resultSet.next()) { reaction.setId(resultSet.getInt(1)); } resultSet.close(); @@ -2649,7 +2657,7 @@ public void saveReactions(Reaction[] reactions, Reaction.Type type, ProgressList } insertStatement.close(); updateStatement.close(); - System.out.println(saveCount+" reactions saved..."); + System.out.println(saveCount + " reactions saved..."); break; case CycleSequencing: insertSQL = "INSERT INTO cyclesequencing (primerName, primerSequence, direction, workflow, plate, location, cocktail, progress, thermocycle, cleanupPerformed, cleanupMethod, extractionId, notes, date, technician, gelimage) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; @@ -2663,42 +2671,39 @@ public void saveReactions(Reaction[] reactions, Reaction.Type type, ProgressList PreparedStatement insertTracesStatement = connection.prepareStatement(insertTracesSQL); for (int i = 0; i < reactions.length; i++) { Reaction reaction = reactions[i]; - if(progress != null) { - progress.setMessage("Saving reaction "+(i+1)+" of "+reactions.length); + if (progress != null) { + progress.setMessage("Saving reaction " + (i + 1) + " of " + reactions.length); } if (!reaction.isEmpty() && reaction.getPlateId() >= 0) { PreparedStatement statement; - if(reaction.getId() >= 0) { //the reaction is already in the database + if (reaction.getId() >= 0) { //the reaction is already in the database statement = updateStatement; statement.setInt(17, reaction.getId()); - } - else { + } else { statement = insertStatement; } ReactionOptions options = reaction.getOptions(); Options.Option option = options.getOption(PCROptions.PRIMER_OPTION_ID); - if(!(option instanceof DocumentSelectionOption)) { - throw new SQLException("Could not save reactions - expected primer type "+DocumentSelectionOption.class.getCanonicalName()+" but found a "+option.getClass().getCanonicalName()); + if (!(option instanceof DocumentSelectionOption)) { + throw new SQLException("Could not save reactions - expected primer type " + DocumentSelectionOption.class.getCanonicalName() + " but found a " + option.getClass().getCanonicalName()); } - List primerOptionValue = ((DocumentSelectionOption)option).getDocuments(); - if(primerOptionValue.size() == 0) { + List primerOptionValue = ((DocumentSelectionOption) option).getDocuments(); + if (primerOptionValue.size() == 0) { statement.setString(1, "None"); statement.setString(2, ""); - } - else { + } else { AnnotatedPluginDocument selectedDoc = primerOptionValue.get(0); - NucleotideSequenceDocument sequence = (NucleotideSequenceDocument)selectedDoc.getDocumentOrThrow(SQLException.class); + NucleotideSequenceDocument sequence = (NucleotideSequenceDocument) selectedDoc.getDocumentOrThrow(SQLException.class); statement.setString(1, selectedDoc.getName()); statement.setString(2, sequence.getSequenceString()); } statement.setString(3, options.getValueAsString("direction")); //statement.setInt(3, (Integer)options.getValue("prAmount")); - if(reaction.getWorkflow() != null) { + if (reaction.getWorkflow() != null) { statement.setInt(4, reaction.getWorkflow().getId()); - } - else { + } else { statement.setObject(4, null); } statement.setInt(5, reaction.getPlateId()); @@ -2707,77 +2712,75 @@ public void saveReactions(Reaction[] reactions, Reaction.Type type, ProgressList Options.OptionValue cocktailValue = (Options.OptionValue) options.getValue("cocktail"); try { cocktailId = Integer.parseInt(cocktailValue.getName()); + } catch (NumberFormatException ex) { + throw new SQLException("The reaction " + reaction.getLocationString() + " does not have a valid cocktail (" + cocktailValue.getLabel() + ", " + cocktailValue.getName() + ")."); } - catch(NumberFormatException ex) { - throw new SQLException("The reaction " + reaction.getLocationString() + " does not have a valid cocktail ("+ cocktailValue.getLabel()+", "+cocktailValue.getName()+")."); - } - if(cocktailId < 0) { - throw new SQLException("The reaction " + reaction.getLocationString() + " does not have a valid cocktail ("+cocktailValue.getName()+")."); + if (cocktailId < 0) { + throw new SQLException("The reaction " + reaction.getLocationString() + " does not have a valid cocktail (" + cocktailValue.getName() + ")."); } statement.setInt(7, cocktailId); - statement.setString(8, ((Options.OptionValue)options.getValue(ReactionOptions.RUN_STATUS)).getLabel()); - if(reaction.getThermocycle() != null) { + statement.setString(8, ((Options.OptionValue) options.getValue(ReactionOptions.RUN_STATUS)).getLabel()); + if (reaction.getThermocycle() != null) { statement.setInt(9, reaction.getThermocycle().getId()); - } - else { + } else { statement.setInt(9, -1); } - statement.setInt(10, ((Options.OptionValue)options.getValue("cleanupPerformed")).getName().equals("true") ? 1 : 0); + statement.setInt(10, ((Options.OptionValue) options.getValue("cleanupPerformed")).getName().equals("true") ? 1 : 0); statement.setString(11, options.getValueAsString("cleanupMethod")); statement.setString(12, reaction.getExtractionId()); statement.setString(13, options.getValueAsString("notes")); - statement.setDate(14, new java.sql.Date(((Date)options.getValue("date")).getTime())); + statement.setDate(14, new java.sql.Date(((Date) options.getValue("date")).getTime())); statement.setString(15, options.getValueAsString("technician")); GelImage image = reaction.getGelImage(); statement.setBytes(16, image != null ? image.getImageBytes() : null); - // List sequences = ((CycleSequencingOptions)options).getTraces(); - // String sequenceString = ""; - // if(sequences != null && sequences.size() > 0) { - // DefaultSequenceListDocument sequenceList = DefaultSequenceListDocument.forNucleotideSequences(sequences); - // Element element = XMLSerializer.classToXML("sequences", sequenceList); - // XMLOutputter out = new XMLOutputter(Format.getCompactFormat()); - // StringWriter writer = new StringWriter(); - // try { - // out.output(element, writer); - // sequenceString = writer.toString(); - // } catch (IOException e) { - // throw new SQLException("Could not write the sequences to the database: "+e.getMessage()); - // } - // } - // - // statement.setString(14, sequenceString); + // List sequences = ((CycleSequencingOptions)options).getTraces(); + // String sequenceString = ""; + // if(sequences != null && sequences.size() > 0) { + // DefaultSequenceListDocument sequenceList = DefaultSequenceListDocument.forNucleotideSequences(sequences); + // Element element = XMLSerializer.classToXML("sequences", sequenceList); + // XMLOutputter out = new XMLOutputter(Format.getCompactFormat()); + // StringWriter writer = new StringWriter(); + // try { + // out.output(element, writer); + // sequenceString = writer.toString(); + // } catch (IOException e) { + // throw new SQLException("Could not write the sequences to the database: "+e.getMessage()); + // } + // } + // + // statement.setString(14, sequenceString); statement.execute(); - if(reaction.getId() < 0) { + if (reaction.getId() < 0) { ResultSet resultSet = getLastId.executeQuery(); - if(resultSet.next()) { + if (resultSet.next()) { reaction.setId(resultSet.getInt(1)); } resultSet.close(); } - if(((CycleSequencingReaction)reaction).getTraces() != null) { + if (((CycleSequencingReaction) reaction).getTraces() != null) { int reactionId = reaction.getId(); - for(Integer traceId : ((CycleSequencingReaction)reaction).getTracesToRemoveOnSave()) { - if(!BiocodeService.getInstance().deleteAllowed("traces")) { + for (Integer traceId : ((CycleSequencingReaction) reaction).getTracesToRemoveOnSave()) { + if (!BiocodeService.getInstance().deleteAllowed("traces")) { throw new SQLException("It appears that you do not have permission to delete traces. Please contact your System Administrator for assistance"); } clearTracesStatement.setInt(1, traceId); clearTracesStatement.execute(); } - ((CycleSequencingReaction)reaction).clearTracesToRemoveOnSave(); - if(reactionId < 0) { + ((CycleSequencingReaction) reaction).clearTracesToRemoveOnSave(); + if (reactionId < 0) { reactionId = getLastInsertId(); } - List traces = ((CycleSequencingReaction)reaction).getTraces(); - if(traces != null) { - for(Trace trace : traces) { - if(trace.getId() >= 0) { + List traces = ((CycleSequencingReaction) reaction).getTraces(); + if (traces != null) { + for (Trace trace : traces) { + if (trace.getId() >= 0) { continue; //already added these... } MemoryFile file = trace.getFile(); - if(file != null) { + if (file != null) { insertTracesStatement.setInt(1, reactionId); insertTracesStatement.setString(2, file.getName()); insertTracesStatement.setBytes(3, file.getData()); @@ -2788,7 +2791,7 @@ public void saveReactions(Reaction[] reactions, Reaction.Type type, ProgressList } } FailureReason reason = FailureReason.getReasonFromOptions(options); - if(reason != null) { + if (reason != null) { // Requires schema 10. This won't work for reactions that don't have an assembly. PreparedStatement update = connection.prepareStatement( "UPDATE assembly SET failure_reason = ? WHERE id IN (" + @@ -2820,16 +2823,16 @@ public void saveReactions(Reaction[] reactions, Reaction.Type type, ProgressList ReactionOptions optionsTemplate = reactions[0].getOptions(); List optionNames = new ArrayList(); for (Options.Option option : optionsTemplate.getOptions()) { - if(option instanceof ButtonOption || option instanceof Options.LabelOption) { + if (option instanceof ButtonOption || option instanceof Options.LabelOption) { continue; } - if(!Arrays.asList("id", "tissueId", "parentExtractionId").contains(option.getName()) + if (!Arrays.asList("id", "tissueId", "parentExtractionId").contains(option.getName()) && !optionsTemplate.fieldIsFinal(option.getName())) { optionNames.add(option.getName()); } } - insertSQL = "INSERT INTO " + GelQuantificationReaction.DB_TABLE_NAME + " (" + StringUtilities.join(",", optionNames) + ",plate,location,gelImage) VALUES" + getQuestionMarksList(optionNames.size()+3); - updateSQL = "UPDATE " + GelQuantificationReaction.DB_TABLE_NAME + " SET "; + insertSQL = "INSERT INTO " + GelQuantificationReaction.DB_TABLE_NAME + " (" + StringUtilities.join(",", optionNames) + ",plate,location,gelImage) VALUES" + getQuestionMarksList(optionNames.size() + 3); + updateSQL = "UPDATE " + GelQuantificationReaction.DB_TABLE_NAME + " SET "; for (String optionName : optionNames) { updateSQL += optionName + "=?,"; @@ -2840,27 +2843,26 @@ public void saveReactions(Reaction[] reactions, Reaction.Type type, ProgressList updateStatement = connection.prepareStatement(updateSQL); for (int i = 0; i < reactions.length; i++) { Reaction reaction = reactions[i]; - if(progress != null) { - progress.setMessage("Saving reaction "+(i+1)+" of "+reactions.length); + if (progress != null) { + progress.setMessage("Saving reaction " + (i + 1) + " of " + reactions.length); } if (!reaction.isEmpty() && reaction.getPlateId() >= 0) { PreparedStatement statement; boolean isUpdateNotInsert = reaction.getId() >= 0; - if(isUpdateNotInsert) { //the reaction is already in the database + if (isUpdateNotInsert) { //the reaction is already in the database statement = updateStatement; - } - else { + } else { statement = insertStatement; } ReactionOptions options = reaction.getOptions(); int columnIndex = 1; for (; columnIndex <= optionNames.size(); columnIndex++) { - String optionName = optionNames.get(columnIndex-1); + String optionName = optionNames.get(columnIndex - 1); Object value = options.getValue(optionName); if (value instanceof Date) { value = new java.sql.Date(((Date) value).getTime()); - } else if(Reaction.EXTRACTION_FIELD.getCode().equals(optionName)) { + } else if (Reaction.EXTRACTION_FIELD.getCode().equals(optionName)) { value = userIdToDatabaseId.get(value); } statement.setObject(columnIndex, value); @@ -2870,16 +2872,16 @@ public void saveReactions(Reaction[] reactions, Reaction.Type type, ProgressList GelImage image = reaction.getGelImage(); statement.setBytes(columnIndex++, image != null ? image.getImageBytes() : null); - if(isUpdateNotInsert) { + if (isUpdateNotInsert) { statement.setInt(columnIndex++, reaction.getId()); } - if(isUpdateNotInsert) { + if (isUpdateNotInsert) { updateStatement.executeUpdate(); } else { insertStatement.executeUpdate(); ResultSet resultSet = getLastId.executeQuery(); - if(resultSet.next()) { + if (resultSet.next()) { reaction.setId(resultSet.getInt(1)); } resultSet.close(); @@ -2908,7 +2910,7 @@ public int addAssembly(boolean isPass, String notes, String technician, FailureR try { connection = getConnection(); statement = connection.prepareStatement("INSERT INTO assembly (extraction_id, workflow, progress, consensus, " + - "coverage, disagreements, trim_params_fwd, trim_params_rev, edits, params, reference_seq_id, confidence_scores, other_processing_fwd, other_processing_rev, notes, technician, bin, ambiguities, editrecord, failure_reason, failure_notes) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,?,?)"); + "coverage, disagreements, trim_params_fwd, trim_params_rev, edits, params, reference_seq_id, confidence_scores, other_processing_fwd, other_processing_rev, notes, technician, bin, ambiguities, editrecord, failure_reason, failure_notes) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,?,?)"); updateReaction = connection.prepareStatement("INSERT INTO sequencing_result(assembly, reaction) VALUES(?,?)"); statement2 = isLocal() ? connection.prepareStatement("CALL IDENTITY();") : connection.prepareStatement("SELECT last_insert_id()"); @@ -2945,17 +2947,15 @@ public int addAssembly(boolean isPass, String notes, String technician, FailureR statement.setString(8, seq.reverseTrimParameters); } statement.setInt(9, seq.numOfEdits); - if(seq.assemblyParameters != null) { + if (seq.assemblyParameters != null) { statement.setString(10, seq.assemblyParameters); //params - } - else { + } else { statement.setNull(10, Types.LONGVARCHAR); //params } statement.setNull(11, Types.INTEGER); //reference_seq_id - if(seq.confidenceScore != null) { + if (seq.confidenceScore != null) { statement.setString(12, seq.confidenceScore); - } - else { + } else { statement.setNull(12, Types.LONGVARCHAR); //confidence_scores } statement.setNull(13, Types.LONGVARCHAR); //other_processing_fwd @@ -2967,25 +2967,23 @@ public int addAssembly(boolean isPass, String notes, String technician, FailureR //technician, date, bin, ambiguities statement.setString(16, technician); - if(seq.bin != null) { + if (seq.bin != null) { statement.setString(17, seq.bin); - } - else { + } else { statement.setNull(17, Types.LONGVARCHAR); } - if(seq.numberOfAmbiguities != null) { + if (seq.numberOfAmbiguities != null) { statement.setInt(18, seq.numberOfAmbiguities); - } - else { + } else { statement.setNull(18, Types.INTEGER); } statement.setString(19, seq.editRecord); - if(failureReason == null) { + if (failureReason == null) { statement.setObject(20, null); } else { statement.setInt(20, failureReason.getId()); } - if(failureNotes == null) { + if (failureNotes == null) { statement.setObject(21, null); } else { statement.setString(21, failureNotes); @@ -2996,8 +2994,8 @@ public int addAssembly(boolean isPass, String notes, String technician, FailureR ResultSet resultSet = statement2.executeQuery(); resultSet.next(); int sequenceId = resultSet.getInt(1); - updateReaction.setObject(1,sequenceId); - for(int reactionId: reactionIds) { + updateReaction.setObject(1, sequenceId); + for (int reactionId : reactionIds) { updateReaction.setObject(2, reactionId); updateReaction.executeUpdate(); } @@ -3008,9 +3006,9 @@ public int addAssembly(boolean isPass, String notes, String technician, FailureR throw new DatabaseServiceException(e, "Failed to park as pass/fail in LIMS: " + e.getMessage(), false); } finally { try { - if(statement != null) + if (statement != null) statement.close(); - if(statement2 != null) + if (statement2 != null) statement2.close(); } catch (SQLException e) { // If we failed to close statements, we'll have to let the garbage collector handle it @@ -3026,7 +3024,7 @@ public boolean deleteAllowed(String tableName) { } private boolean privilegeAllowed(String tableName, String grantToCheckFor) { - if(isLocal() || getUsername().toLowerCase().equals("root")) { + if (isLocal() || getUsername().toLowerCase().equals("root")) { return true; } @@ -3037,16 +3035,15 @@ private boolean privilegeAllowed(String tableName, String grantToCheckFor) { ResultSet resultSet = createStatement("SHOW GRANTS FOR CURRENT_USER").executeQuery(); - while(resultSet.next()) { + while (resultSet.next()) { String grantString = resultSet.getString(1); - if(isGrantStringForMySQLDatabase(grantString, databaseName, tableName)) { - if(grantString.contains("ALL") || grantString.matches(".*" + grantToCheckFor + "(,|\\s+ON).*")) { + if (isGrantStringForMySQLDatabase(grantString, databaseName, tableName)) { + if (grantString.contains("ALL") || grantString.matches(".*" + grantToCheckFor + "(,|\\s+ON).*")) { return true; } } } - } - catch(SQLException ex) { + } catch (SQLException ex) { // todo ex.printStackTrace(); assert false : ex.getMessage(); @@ -3104,7 +3101,7 @@ public void addCocktails(List cocktails) throws DatabaseServ ConnectionWrapper connection = null; try { connection = getConnection(); - for(Cocktail cocktail : cocktails) { + for (Cocktail cocktail : cocktails) { connection.executeUpdate(cocktail.getSQLString()); } connection.close(); @@ -3120,22 +3117,22 @@ public void deleteCocktails(List deletedCocktails) throws Da ConnectionWrapper connection = null; try { connection = getConnection(); - if(!BiocodeService.getInstance().deleteAllowed("cocktail")) { + if (!BiocodeService.getInstance().deleteAllowed("cocktail")) { throw new DatabaseServiceException("It appears that you do not have permission to delete cocktails. Please contact your System Administrator for assistance", false); } - for(Cocktail cocktail : deletedCocktails) { - String sql = "DELETE FROM "+cocktail.getTableName()+" WHERE id = ?"; + for (Cocktail cocktail : deletedCocktails) { + String sql = "DELETE FROM " + cocktail.getTableName() + " WHERE id = ?"; PreparedStatement statement = connection.prepareStatement(sql); - if(cocktail.getId() >= 0) { - if(getPlatesUsingCocktail(cocktail.getReactionType(), cocktail.getId()).size() > 0) { - throw new DatabaseServiceException("The cocktail "+cocktail.getName()+" is in use by reactions in your database. Only unused cocktails can be removed.", false); + if (cocktail.getId() >= 0) { + if (getPlatesUsingCocktail(cocktail.getReactionType(), cocktail.getId()).size() > 0) { + throw new DatabaseServiceException("The cocktail " + cocktail.getName() + " is in use by reactions in your database. Only unused cocktails can be removed.", false); } statement.setInt(1, cocktail.getId()); statement.executeUpdate(); } } } catch (SQLException e) { - throw new DatabaseServiceException(e, "Could not delete cocktails: "+e.getMessage(), false); + throw new DatabaseServiceException(e, "Could not delete cocktails: " + e.getMessage(), false); } finally { returnConnection(connection); } @@ -3147,12 +3144,11 @@ public List getPCRCocktailsFromDatabase() throws DatabaseServiceExc try { connection = getConnection(); ResultSet resultSet = connection.executeQuery("SELECT * FROM pcr_cocktail"); - while(resultSet.next()) { + while (resultSet.next()) { cocktails.add(new PCRCocktail(resultSet)); } resultSet.getStatement().close(); - } - catch(SQLException ex) { + } catch (SQLException ex) { ex.printStackTrace(); throw new DatabaseServiceException(ex, "Could not query PCR Cocktails from the database", false); } finally { @@ -3168,12 +3164,11 @@ public List getCycleSequencingCocktailsFromDatabase() t try { connection = getConnection(); ResultSet resultSet = connection.executeQuery("SELECT * FROM cyclesequencing_cocktail"); - while(resultSet.next()) { + while (resultSet.next()) { cocktails.add(new CycleSequencingCocktail(resultSet)); } resultSet.getStatement().close(); - } - catch(SQLException ex) { + } catch (SQLException ex) { throw new DatabaseServiceException(ex, "Could not query CycleSequencing Cocktails from the database", false); } finally { returnConnection(connection); @@ -3182,7 +3177,7 @@ public List getCycleSequencingCocktailsFromDatabase() t } public List getThermocyclesFromDatabase(Thermocycle.Type type) throws DatabaseServiceException { - String sql = "SELECT * FROM "+type.databaseTable+" LEFT JOIN thermocycle ON (thermocycle.id = "+type.databaseTable+".cycle) LEFT JOIN cycle ON (thermocycle.id = cycle.thermocycleId) LEFT JOIN state ON (cycle.id = state.cycleId);"; + String sql = "SELECT * FROM " + type.databaseTable + " LEFT JOIN thermocycle ON (thermocycle.id = " + type.databaseTable + ".cycle) LEFT JOIN cycle ON (thermocycle.id = cycle.thermocycleId) LEFT JOIN state ON (cycle.id = state.cycleId);"; System.out.println(sql); List tCycles = new ArrayList(); @@ -3192,22 +3187,20 @@ public List getThermocyclesFromDatabase(Thermocycle.Type type) thro connection = getConnection(); ResultSet resultSet = connection.executeQuery(sql); resultSet.next(); - while(true) { + while (true) { try { Thermocycle thermocycle = Thermocycle.fromSQL(resultSet); - if(thermocycle != null) { + if (thermocycle != null) { tCycles.add(thermocycle); - } - else { + } else { break; } - } - catch(SQLException e) { + } catch (SQLException e) { break; } } resultSet.getStatement().close(); - } catch(SQLException ex) { + } catch (SQLException ex) { throw new DatabaseServiceException(ex, "could not read thermocycles from the database", false); } finally { returnConnection(connection); @@ -3222,7 +3215,7 @@ public void addThermoCycles(Thermocycle.Type type, List cycles) thr try { connection = getConnection(); connection.beginTransaction(); - for(Thermocycle tCycle : cycles) { + for (Thermocycle tCycle : cycles) { int id = addThermoCycle(connection, tCycle); PreparedStatement statement = connection.prepareStatement("INSERT INTO " + type.databaseTable + " (cycle) VALUES (" + id + ");\n"); statement.execute(); @@ -3230,7 +3223,7 @@ public void addThermoCycles(Thermocycle.Type type, List cycles) thr } connection.endTransaction(); } catch (SQLException e) { - throw new DatabaseServiceException(e, "Could not add thermocycle(s): "+e.getMessage(), false); + throw new DatabaseServiceException(e, "Could not add thermocycle(s): " + e.getMessage(), false); } finally { returnConnection(connection); } @@ -3253,7 +3246,7 @@ public static int addThermoCycle(ConnectionWrapper connection, Thermocycle therm int thermoId = resultSet.getInt(1); statement.close(); - for(Thermocycle.Cycle cycle : thermocycle.getCycles()) { + for (Thermocycle.Cycle cycle : thermocycle.getCycles()) { //create a cycle record PreparedStatement statement2 = connection.prepareStatement("INSERT INTO cycle (thermocycleid, repeats) VALUES (" + thermoId + ", " + cycle.getRepeats() + ");\n"); statement2.execute(); @@ -3261,14 +3254,14 @@ public static int addThermoCycle(ConnectionWrapper connection, Thermocycle therm //get the id of the cycle record statement = BiocodeService.getInstance().getActiveLIMSConnection().isLocal() ? - connection.prepareStatement("CALL IDENTITY();") : - connection.prepareStatement("SELECT last_insert_id()"); + connection.prepareStatement("CALL IDENTITY();") : + connection.prepareStatement("SELECT last_insert_id()"); resultSet = statement.executeQuery(); resultSet.next(); int cycleId = resultSet.getInt(1); statement.close(); - for(Thermocycle.State state : cycle.getStates()) { + for (Thermocycle.State state : cycle.getStates()) { //create the state record PreparedStatement statement3 = connection.prepareStatement("INSERT INTO state (cycleid, temp, length) VALUES (" + cycleId + ", " + state.getTemp() + ", " + state.getTime() + ");\n"); statement3.execute(); @@ -3284,13 +3277,13 @@ public void deleteThermoCycles(Thermocycle.Type type, List cycles) String sql = "DELETE FROM state WHERE state.id=?"; String sql2 = "DELETE FROM cycle WHERE cycle.id=?"; String sql3 = "DELETE FROM thermocycle WHERE thermocycle.id=?"; - String sql4 = "DELETE FROM "+type.databaseTable+" WHERE cycle =?"; + String sql4 = "DELETE FROM " + type.databaseTable + " WHERE cycle =?"; ConnectionWrapper connection = null; try { - if(!BiocodeService.getInstance().deleteAllowed("state") || - !BiocodeService.getInstance().deleteAllowed("cycle") || - !BiocodeService.getInstance().deleteAllowed("thermocycle") || - !BiocodeService.getInstance().deleteAllowed(type.databaseTable)) { + if (!BiocodeService.getInstance().deleteAllowed("state") || + !BiocodeService.getInstance().deleteAllowed("cycle") || + !BiocodeService.getInstance().deleteAllowed("thermocycle") || + !BiocodeService.getInstance().deleteAllowed(type.databaseTable)) { throw new DatabaseServiceException("It appears that you do not have permission to delete thermocycles. Please contact your System Administrator for assistance", false); } @@ -3300,13 +3293,13 @@ public void deleteThermoCycles(Thermocycle.Type type, List cycles) final PreparedStatement statement2 = connection.prepareStatement(sql2); final PreparedStatement statement3 = connection.prepareStatement(sql3); final PreparedStatement statement4 = connection.prepareStatement(sql4); - for(Thermocycle thermocycle : cycles) { - if(thermocycle.getId() >= 0) { - if(getPlatesUsingThermocycle(thermocycle.getId()).size() > 0) { - throw new DatabaseServiceException("The thermocycle "+thermocycle.getName()+" is being used by plates in your database. Only unused thermocycles can be removed", false); + for (Thermocycle thermocycle : cycles) { + if (thermocycle.getId() >= 0) { + if (getPlatesUsingThermocycle(thermocycle.getId()).size() > 0) { + throw new DatabaseServiceException("The thermocycle " + thermocycle.getName() + " is being used by plates in your database. Only unused thermocycles can be removed", false); } - for(Thermocycle.Cycle cycle : thermocycle.getCycles()) { - for(Thermocycle.State state : cycle.getStates()) { + for (Thermocycle.Cycle cycle : thermocycle.getCycles()) { + for (Thermocycle.State state : cycle.getStates()) { statement.setInt(1, state.getId()); statement.executeUpdate(); } @@ -3321,7 +3314,7 @@ public void deleteThermoCycles(Thermocycle.Type type, List cycles) } } catch (SQLException e) { e.printStackTrace(); - throw new DatabaseServiceException(e, "Could not delete thermocycles: "+e.getMessage(), false); + throw new DatabaseServiceException(e, "Could not delete thermocycles: " + e.getMessage(), false); } finally { returnConnection(connection); } @@ -3377,7 +3370,7 @@ protected void close() throws SQLException { } private static void closeConnection(ConnectionWrapper connection) { - if(connection != null) { + if (connection != null) { try { connection.close(); } catch (SQLException e) { @@ -3420,7 +3413,7 @@ public void deleteSequences(List sequencesToDelete) throws DatabaseServ StringBuilder sql = new StringBuilder("DELETE FROM assembly WHERE ("); for (int i1 = 0; i1 < sequencesToDelete.size(); i1++) { sql.append("id=?"); - if(i1 < sequencesToDelete.size()-1) { + if (i1 < sequencesToDelete.size() - 1) { sql.append(" OR "); } } @@ -3432,15 +3425,15 @@ public void deleteSequences(List sequencesToDelete) throws DatabaseServ for (int i1 = 0; i1 < sequencesToDelete.size(); i1++) { Integer i = sequencesToDelete.get(i1); - statement.setInt(i1+1, i); + statement.setInt(i1 + 1, i); } int notDeletedCount = sequencesToDelete.size() - statement.executeUpdate(); - if(notDeletedCount > 0) { + if (notDeletedCount > 0) { throw new DatabaseServiceException(notDeletedCount + " sequences were not deleted.", false); } - } catch(SQLException e) { - throw new DatabaseServiceException(e, "Could not delete sequences: "+e.getMessage(), true); + } catch (SQLException e) { + throw new DatabaseServiceException(e, "Could not delete sequences: " + e.getMessage(), true); } finally { returnConnection(connection); } @@ -3457,7 +3450,7 @@ public void deleteSequencesForWorkflowId(Integer workflowId, String extractionId statement.setInt(1, workflowId); statement.setString(2, extractionId); statement.executeUpdate(); - } catch(SQLException e) { + } catch (SQLException e) { throw new DatabaseServiceException(e, e.getMessage(), false); } finally { returnConnection(connection); @@ -3473,14 +3466,14 @@ public void setSequenceStatus(boolean submitted, List ids) throws Datab SqlUtilities.appendSetOfQuestionMarks(query, ids.size()); PreparedStatement statement1 = connection.prepareStatement(query.toString()); statement1.setString(1, "passed"); - for(int i=0; i < ids.size(); i++) { - statement1.setInt(i+2, ids.get(i)); + for (int i = 0; i < ids.size(); i++) { + statement1.setInt(i + 2, ids.get(i)); } ResultSet set = statement1.executeQuery(); set.next(); int count = set.getInt(1); - if(count < ids.size()) { + if (count < ids.size()) { throw new DatabaseServiceException("Some of the sequences you are marking are either not present in the database, or are marked as failed. Please make sure that the sequences are present, and are passed before marking as submitted.", false); } @@ -3489,13 +3482,13 @@ public void setSequenceStatus(boolean submitted, List ids) throws Datab PreparedStatement statement2 = connection.prepareStatement(updateString.toString()); statement2.setInt(1, submitted ? 1 : 0); - for(int i=0; i < ids.size(); i++) { - statement2.setInt(i+2, ids.get(i)); + for (int i = 0; i < ids.size(); i++) { + statement2.setInt(i + 2, ids.get(i)); } statement2.executeUpdate(); } catch (SQLException e) { - throw new DatabaseServiceException(e, "There was a problem marking as submitted in LIMS: "+e.getMessage(), false); + throw new DatabaseServiceException(e, "There was a problem marking as submitted in LIMS: " + e.getMessage(), false); } finally { returnConnection(connection); } @@ -3503,7 +3496,7 @@ public void setSequenceStatus(boolean submitted, List ids) throws Datab @Override public Map getTissueIdsForExtractionIds_(String tableName, List extractionIds) throws DatabaseServiceException { - String tableDefinition = tableName.equals("extraction") ? tableName : tableName+", extraction, workflow"; + String tableDefinition = tableName.equals("extraction") ? tableName : tableName + ", extraction, workflow"; String notExtractionBit = tableName.equals("extraction") ? "" : " workflow.extractionId = extraction.id AND " + tableName + ".workflow = workflow.id AND"; StringBuilder sql = new StringBuilder("SELECT extraction.extractionId AS extractionId, extraction.sampleId AS tissue FROM " + tableDefinition + " WHERE" + notExtractionBit + " ("); @@ -3516,7 +3509,7 @@ public Map getTissueIdsForExtractionIds_(String tableName, List< count++; } sql.append(")"); - if(count == 0) { + if (count == 0) { return Collections.emptyMap(); } @@ -3533,7 +3526,7 @@ public Map getTissueIdsForExtractionIds_(String tableName, List< ResultSet resultSet = statement.executeQuery(); Map results = new HashMap(); - while(resultSet.next()) { + while (resultSet.next()) { results.put(resultSet.getString("extractionId"), resultSet.getString("tissue")); } @@ -3635,7 +3628,7 @@ public Map> downloadTraces(List reactionIDs, @Override public List getExtractionsForIds_(List extractionIds) throws DatabaseServiceException { List extractionsThatExist = new ArrayList(); - if(extractionIds.isEmpty()) { + if (extractionIds.isEmpty()) { return extractionsThatExist; } @@ -3647,12 +3640,12 @@ public List getExtractionsForIds_(List extractionIds try { connection = getConnection(); PreparedStatement statement = connection.prepareStatement(sql.toString()); - int count=1; - for(String extractionId : extractionIds) { + int count = 1; + for (String extractionId : extractionIds) { statement.setString(count++, extractionId); } ResultSet results = statement.executeQuery(); - while(results.next()) { + while (results.next()) { extractionsThatExist.add(new ExtractionReaction(results)); } statement.close(); @@ -3670,6 +3663,6 @@ public boolean supportReporting() { } static boolean isGrantStringForMySQLDatabase(String grantString, String databaseName, String tableName) { - return grantString.matches("GRANT.*ON\\s+(`?(("+ databaseName + ")|%|\\*)`?\\.)?`?(\\*|"+ tableName + ")`?\\s+TO.*"); + return grantString.matches("GRANT.*ON\\s+(`?((" + databaseName + ")|%|\\*)`?\\.)?`?(\\*|" + tableName + ")`?\\s+TO.*"); } } \ No newline at end of file diff --git a/src/com/biomatters/plugins/biocode/labbench/plates/PlateViewer.java b/src/com/biomatters/plugins/biocode/labbench/plates/PlateViewer.java index 1dd1a29a5..202f2670b 100644 --- a/src/com/biomatters/plugins/biocode/labbench/plates/PlateViewer.java +++ b/src/com/biomatters/plugins/biocode/labbench/plates/PlateViewer.java @@ -111,7 +111,7 @@ public void itemStateChanged(ItemEvent e) { leftToolbar.add(new JLabel("Thermocycle: ")); leftToolbar.add(thermocycleCombo); - final GeneiousAction thermocycleAction = new GeneiousAction("View/Add Thermocycles", "Create new Thermocycles, or view existing ones", BiocodePlugin.getIcons("thermocycle_16.png")) { + final GeneiousAction thermocycleAction = new GeneiousAction("View/Add Thermocycles", "Create or view Thermocycles", BiocodePlugin.getIcons("thermocycle_16.png")) { public void actionPerformed(ActionEvent e) { ThermocycleEditor editor = new ThermocycleEditor(); if(!editor.editThermocycles(getThermocycles(), selfReference)) { diff --git a/src/com/biomatters/plugins/biocode/labbench/reaction/ExtractionReaction.java b/src/com/biomatters/plugins/biocode/labbench/reaction/ExtractionReaction.java index 0e949916c..7a5cf3a31 100644 --- a/src/com/biomatters/plugins/biocode/labbench/reaction/ExtractionReaction.java +++ b/src/com/biomatters/plugins/biocode/labbench/reaction/ExtractionReaction.java @@ -329,21 +329,21 @@ private static String checkForExistingExtractionReactionsAssociatedWithAttribute if (!existingExtractionReactionsToNewExtractionReactions.isEmpty()) { String attributeName = reactionAttributeGetter.getAttributeName(); if (checkingFromPlate) { - if (Dialogs.showYesNoDialog( - "Extraction reactions that are associated with the following " + attributeName.toLowerCase() + "(s) already exist: " + StringUtilities.join(", ", attributeToExistingExtractionReactions.keySet()) + "." - + "

Move data to new/edited extraction reactions from corresponding existing ones?", - "Move Extraction Reactions?", - dialogParent, - Dialogs.DialogIcon.QUESTION)) { - return overrideExtractionReactionsWithExistingExtractionReactionsWithSameAttribute(existingExtractionReactionsToNewExtractionReactions, reactionAttributeGetter); - } else { - List newExtractionReactionsAssociatedWithExistingAttributeValue = new ArrayList(); - for (List groupOfNewExtractionReactionsAssociatedWithSameExistingBarcode : existingExtractionReactionsToNewExtractionReactions.values()) { - newExtractionReactionsAssociatedWithExistingAttributeValue.addAll(groupOfNewExtractionReactionsAssociatedWithSameExistingBarcode); - } + String move_extractions = "Move extractions"; + String create_alliquots = "Create aliquots"; - ReactionUtilities.setReactionErrorStates(newExtractionReactionsAssociatedWithExistingAttributeValue, true); + String firstPartOfMessage = attributeToExistingExtractionReactions.keySet().size() < 4 ? "Extraction reactions that are associated with the following " + attributeName.toLowerCase() + "(s) already exist: " + StringUtilities.join(", ", attributeToExistingExtractionReactions.keySet()) + ".

" + : "Extraction reactions that are associated with "+attributeToExistingExtractionReactions.keySet().size()+" " + attributeName.toLowerCase() + "(s) you entered already exist. "; + + boolean b = Dialogs.showDialog(new Dialogs.DialogOptions(new String[] {move_extractions, create_alliquots}, "Extractions already exist", dialogParent, Dialogs.DialogIcon.QUESTION), firstPartOfMessage + "Are you trying to:

Move these extractions to this plate? The existing extraction reactions will be removed from their original plate and placed on this one. " + + "Their original locations will be tracked in the previous plate and previous well fields." + + "

Create aliquots? The existing extraction reactions will be left untouched, and these ones will be given new extraction id's. The location and id's of each alliquot's parent extractions will be tracked in the parent extraction id, " + + "previous plate, and previous well fields.") == move_extractions; + if (b) { + return overrideExtractionReactionsWithExistingExtractionReactionsWithSameAttribute(existingExtractionReactionsToNewExtractionReactions, reactionAttributeGetter, false); + } else { + overrideExtractionReactionsWithExistingExtractionReactionsWithSameAttribute(existingExtractionReactionsToNewExtractionReactions, reactionAttributeGetter, true); } } else { Dialogs.showMessageDialog( @@ -396,13 +396,38 @@ private static Map, List> buildExis } private static String overrideExtractionReactionsWithExistingExtractionReactionsWithSameAttribute(Map, List> existingExtractionReactionsToNewExtractionReactions, - ReactionAttributeGetter reactionAttributeGetter) { + ReactionAttributeGetter reactionAttributeGetter, boolean copyInsteadOfMove) throws DatabaseServiceException { List extractionReactionsThatCouldNotBeOverridden = new ArrayList(); + Set existingExtractionIds = new LinkedHashSet<>(); + + if(copyInsteadOfMove) { + LIMSConnection activeLIMSConnection = BiocodeService.getInstance().getActiveLIMSConnection(); + Set tissueIds = new LinkedHashSet<>(); + for (List existingExtractionReactions : existingExtractionReactionsToNewExtractionReactions.keySet()) { + for(ExtractionReaction reaction : existingExtractionReactions) { + tissueIds.add(reaction.getTissueId()); + } + } + + existingExtractionIds.addAll(activeLIMSConnection.getAllExtractionIdsForTissueIds(new ArrayList<>(tissueIds))); + } + for (Map.Entry, List> existingExtractionReactionsAndNewExtractionReactions : existingExtractionReactionsToNewExtractionReactions.entrySet()) { List newExtractionReactions = existingExtractionReactionsAndNewExtractionReactions.getValue(); - if (newExtractionReactions.size() > 1) { + if(copyInsteadOfMove) { + for(ExtractionReaction destinationReaction : newExtractionReactions) { + ExtractionReaction.copyExtractionReaction(getExistingExtractionReactionToMove(existingExtractionReactionsAndNewExtractionReactions.getKey()), destinationReaction); + + destinationReaction.setExtractionId(ReactionUtilities.getNewExtractionId(existingExtractionIds, destinationReaction.getTissueId())); + + existingExtractionIds.add(destinationReaction.getExtractionId()); + + destinationReaction.setJustMoved(true); + } + } + else if (newExtractionReactions.size() > 1) { ReactionUtilities.setReactionErrorStates(newExtractionReactions, true); extractionReactionsThatCouldNotBeOverridden.add(reactionAttributeGetter.getAttributeName() + ": " + reactionAttributeGetter.get(newExtractionReactions.get(0)) + ".\n" + "Well Numbers: " + StringUtilities.join(", ", ReactionUtilities.getWellNumbers(newExtractionReactions)) + "."); @@ -411,6 +436,8 @@ private static String overrideExtractionReactionsWithExistingExtractionReactions ExtractionReaction.copyExtractionReaction(getExistingExtractionReactionToMove(existingExtractionReactionsAndNewExtractionReactions.getKey()), destinationReaction); + destinationReaction.getOptions().setValue("parentExtraction", ""); + destinationReaction.setJustMoved(true); } } diff --git a/test/com/biomatters/plugins/biocode/labbench/NewPlateDocumentOperationTest.java b/test/com/biomatters/plugins/biocode/labbench/NewPlateDocumentOperationTest.java index f935f0a0d..160bb76c5 100644 --- a/test/com/biomatters/plugins/biocode/labbench/NewPlateDocumentOperationTest.java +++ b/test/com/biomatters/plugins/biocode/labbench/NewPlateDocumentOperationTest.java @@ -36,9 +36,6 @@ public class NewPlateDocumentOperationTest extends LimsTestCase { private static final String THREE_HUNDRED_EIGHTY_FOUR_WELL_PLATE_TYPE_VALUE = "384Plate"; //TODO: Fix these tests which are failing due to Caused by: java.lang.IllegalArgumentException: Key too long: buttonVisibleView/Add Thermocycles:Create new Thermocycles, or view existing ones - - /* - @Test public void testCreateStripExtractionPlateFromStripExtractionPlate() throws DocumentOperationException, DatabaseServiceException, BadDataException { for (int numberOfStrips = 1; numberOfStrips <= 6; numberOfStrips++) { @@ -189,19 +186,19 @@ public void testCustomCopyInSmithsonianGelQuantificationFormat() throws Document assertEquals(4, gelPlates.size()); // There should be 4x 30-well gel quantification plates from a single 96-well plate. } - @Test - public void testCustomCopyWhenSourceHasMoreWellsThanDestination() throws DocumentOperationException, DatabaseServiceException, BadDataException { - Plate extractionPlate = createExtractionNonEmptyExtractionPlate(NINETY_SIX_WELL_PLATE_TYPE_VALUE, 0); - AnnotatedPluginDocument annotatedPlate = DocumentUtilities.createAnnotatedPluginDocument(new PlateDocument(extractionPlate)); - NewPlateOptions options = createNewPlateOptions(Reaction.Type.GelQuantification.name, INDIVIDUAL_REACTIONS_PLATE_TYPE_VALUE, 20, annotatedPlate); - options.setValue(NewPlateOptions.USE_CUSTOM_COPY, true); + //@Test + //public void testCustomCopyWhenSourceHasMoreWellsThanDestination() throws DocumentOperationException, DatabaseServiceException, BadDataException { + // Plate extractionPlate = createExtractionNonEmptyExtractionPlate(NINETY_SIX_WELL_PLATE_TYPE_VALUE, 0); + // AnnotatedPluginDocument annotatedPlate = DocumentUtilities.createAnnotatedPluginDocument(new PlateDocument(extractionPlate)); + // NewPlateOptions options = createNewPlateOptions(Reaction.Type.GelQuantification.name, INDIVIDUAL_REACTIONS_PLATE_TYPE_VALUE, 20, annotatedPlate); + // options.setValue(NewPlateOptions.USE_CUSTOM_COPY, true); - for (int start : Arrays.asList(19, 20, 21)) { - for (Options.OptionValue insertMethod : Arrays.asList(NewPlateOptions.ALTERNATING, NewPlateOptions.SEQUENTIALLY)) { - testCustomCopy(extractionPlate, Arrays.asList(Reaction.Type.values()), insertMethod, start, Arrays.asList(1,2,3), null); - } - } - } + // for (int start : Arrays.asList(19, 20, 21)) { + // for (Options.OptionValue insertMethod : Arrays.asList(NewPlateOptions.ALTERNATING, NewPlateOptions.SEQUENTIALLY)) { + // testCustomCopy(extractionPlate, Arrays.asList(Reaction.Type.values()), insertMethod, start, Arrays.asList(1,2,3), null); + // } + // } + //} private static int extractionPlateCount = 1; private static Plate createExtractionNonEmptyExtractionPlate(String plateType, int plateValue) throws DocumentOperationException, DatabaseServiceException, BadDataException { @@ -277,5 +274,5 @@ private static NewPlateOptions createNewPlateOptions(String reactionType, String return newPlateOptions; } - */ + } \ No newline at end of file diff --git a/test/com/biomatters/plugins/biocode/labbench/plates/PlateBulkEditorTest.java b/test/com/biomatters/plugins/biocode/labbench/plates/PlateBulkEditorTest.java index 3991aacf0..e1aedc077 100644 --- a/test/com/biomatters/plugins/biocode/labbench/plates/PlateBulkEditorTest.java +++ b/test/com/biomatters/plugins/biocode/labbench/plates/PlateBulkEditorTest.java @@ -15,7 +15,7 @@ */ public class PlateBulkEditorTest extends LimsTestCase { // TODO: figure out Unable to XML deserialize multiple options. This is most likely because the child multiple options do not fulfil the XMLSerializable contract. - /* + @Test public void testDocumentFieldEditorGetChanges() { PlateBulkEditor.DocumentFieldEditor documentFieldEditor = new PlateBulkEditor.DocumentFieldEditor(PlateBulkEditor.WORKFLOW_ID_FIELD, new Plate(Plate.Size.w16, Reaction.Type.PCR), PlateBulkEditor.Direction.ACROSS_AND_DOWN, null); @@ -71,18 +71,18 @@ public void testDocumentFieldEditorGetChanges() { assertEquals("H1 : 14 => 15", changes.get(14)); assertEquals("H2 : 15 => 16", changes.get(15)); } - */ + // TODO: figure out Unable to XML deserialize multiple options. This is most likely because the child multiple options do not fulfil the XMLSerializable contract. - //@Test - //public void testBuldEdit_AddBarcodesFromFile_splitByDelimiters() { - // PlateBulkEditor.DocumentFieldEditor barcodeEditor = new PlateBulkEditor.DocumentFieldEditor(PlateBulkEditor.EXTRACTION_BARCODE_FIELD, new Plate(Plate.Size.w1, Reaction.Type.PCR), PlateBulkEditor.Direction.ACROSS_AND_DOWN, null); - // BiocodeUtilities.Well well = new BiocodeUtilities.Well("A1"); - // - // List delimiters = Arrays.asList("\t", ";", ",", " ", "-", "_", /*Try some combinations:*/ ";\t", ",\t", "-;"); - // for (int i = 0; i < delimiters.size(); i++) { - // PlateBulkEditor.setWellAndBarcode(String.format("A1%s%d", delimiters.get(i), i+2), barcodeEditor); - /* Object value = barcodeEditor.getValue(well.row(), well.col()); + @Test + public void testBuldEdit_AddBarcodesFromFile_splitByDelimiters() { + PlateBulkEditor.DocumentFieldEditor barcodeEditor = new PlateBulkEditor.DocumentFieldEditor(PlateBulkEditor.EXTRACTION_BARCODE_FIELD, new Plate(Plate.Size.w1, Reaction.Type.PCR), PlateBulkEditor.Direction.ACROSS_AND_DOWN, null); + BiocodeUtilities.Well well = new BiocodeUtilities.Well("A1"); + + List delimiters = Arrays.asList("\t", ";", ",", " ", "-", "_", /*Try some combinations:*/ ";\t", ",\t", "-;"); + for (int i = 0; i < delimiters.size(); i++) { + PlateBulkEditor.setWellAndBarcode(String.format("A1%s%d", delimiters.get(i), i+2), barcodeEditor); + Object value = barcodeEditor.getValue(well.row(), well.col()); assertEquals(""+(i+2), value); } barcodeEditor.setValue(well.row(), well.col(), "0"); @@ -94,7 +94,7 @@ public void testDocumentFieldEditorGetChanges() { assertEquals("0", value); } } - */ + private static String getNewLineSeparatedSequenceOfNumbers(int start, int end) { if (start >= end) {