diff --git a/.classpath b/.classpath
index 1f3a0f9..1e64e60 100644
--- a/.classpath
+++ b/.classpath
@@ -6,5 +6,52 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ECOLIISO.batch b/ECOLIISO.batch
new file mode 100644
index 0000000..c597512
Binary files /dev/null and b/ECOLIISO.batch differ
diff --git a/ECOLIISO.cfg b/ECOLIISO.cfg
new file mode 100644
index 0000000..65e499b
Binary files /dev/null and b/ECOLIISO.cfg differ
diff --git a/ECOLIISO.dat b/ECOLIISO.dat
new file mode 100644
index 0000000..665a849
Binary files /dev/null and b/ECOLIISO.dat differ
diff --git a/ECOLIISO.meta b/ECOLIISO.meta
new file mode 100644
index 0000000..73d8c23
--- /dev/null
+++ b/ECOLIISO.meta
@@ -0,0 +1,16 @@
+RecordId
+SampleId
+DateOfSampling
+DateOfReceiptSourceLab
+DateOfReceiptReferenceLab
+Gender=M,F,UNK
+Age
+AgeMonth
+SampleOrigin
+Specimen
+PlaceOfResidence
+Imported=Y,N,UNK
+ProbableCountryOfInfection
+Serotype
+WgsProtocol=MISEQ_2X300,MISEQ_2x250,MISEQ_2X150,NEXTSEQ_2X150,HISEQ_2X100,MINISEQ_2X150,IONTORRENT
+WgsAssembler=SPADES,VELVET,MAP_TO_LOCI1
\ No newline at end of file
diff --git a/ERC000028.txt b/ERC000028.txt
new file mode 100644
index 0000000..0e365c1
--- /dev/null
+++ b/ERC000028.txt
@@ -0,0 +1,7 @@
+serovar
+collected_by
+collection_date
+country
+host_associated
+specific_host
+host_disease_status
\ No newline at end of file
diff --git a/ERC000028b.txt b/ERC000028b.txt
new file mode 100644
index 0000000..4b9a898
--- /dev/null
+++ b/ERC000028b.txt
@@ -0,0 +1,8 @@
+serovar
+collected_by
+collection_date
+country
+host_associated
+specific_host
+host_disease_status
+test
diff --git a/ERC000044.txt b/ERC000044.txt
new file mode 100644
index 0000000..ef7728e
--- /dev/null
+++ b/ERC000044.txt
@@ -0,0 +1,4 @@
+collection_date
+collected_by
+geographic location (country and/or sea)
+host scientific name
\ No newline at end of file
diff --git a/ERC000045.txt b/ERC000045.txt
new file mode 100644
index 0000000..2f801c8
--- /dev/null
+++ b/ERC000045.txt
@@ -0,0 +1,2 @@
+collection_date
+isolation_source
\ No newline at end of file
diff --git a/LISTISO.batch b/LISTISO.batch
new file mode 100644
index 0000000..cbd3bb4
Binary files /dev/null and b/LISTISO.batch differ
diff --git a/LISTISO.cfg b/LISTISO.cfg
new file mode 100644
index 0000000..cebf739
Binary files /dev/null and b/LISTISO.cfg differ
diff --git a/LISTISO.dat b/LISTISO.dat
new file mode 100644
index 0000000..061ca12
Binary files /dev/null and b/LISTISO.dat differ
diff --git a/LISTISO.meta b/LISTISO.meta
new file mode 100644
index 0000000..4e9875c
--- /dev/null
+++ b/LISTISO.meta
@@ -0,0 +1,15 @@
+RecordId
+SampleId
+DateOfSampling
+DateOfReceiptSourceLab
+DateOfReceiptReferenceLab
+Gender=M,F,UNK
+Age
+AgeMonth
+SampleOrigin
+Specimen
+PlaceOfResidence
+Imported=Y,N,UNK
+ProbableCountryOfInfection
+WgsProtocol=MISEQ_2X300,MISEQ_2x250,MISEQ_2X150,NEXTSEQ_2X150,HISEQ_2X100,MINISEQ_2X150,IONTORRENT
+WgsAssembler=SPADES,VELVET,MAP_TO_LOCI1
\ No newline at end of file
diff --git a/SALMISO.batch b/SALMISO.batch
new file mode 100644
index 0000000..c597512
Binary files /dev/null and b/SALMISO.batch differ
diff --git a/SALMISO.cfg b/SALMISO.cfg
new file mode 100644
index 0000000..3f43da5
Binary files /dev/null and b/SALMISO.cfg differ
diff --git a/SALMISO.dat b/SALMISO.dat
new file mode 100644
index 0000000..c5f1937
Binary files /dev/null and b/SALMISO.dat differ
diff --git a/SALMISO.meta b/SALMISO.meta
new file mode 100644
index 0000000..41b38c0
--- /dev/null
+++ b/SALMISO.meta
@@ -0,0 +1,16 @@
+RecordId
+SampleId
+DateOfSampling
+DateOfReceiptSourceLab
+DateOfReceiptReferenceLab
+Gender=M,F,UNK
+Age
+AgeMonth
+SampleOrigin
+Specimen
+PlaceOfResidence
+Imported
+ProbableCountryOfInfection
+Serotype
+WgsProtocol=MISEQ_2X300,MISEQ_2x250,MISEQ_2X150,NEXTSEQ_2X150,HISEQ_2X100,MINISEQ_2X150,IONTORRENT
+WgsAssembler=SPADES,VELVET,MAP_TO_LOCI1
diff --git a/TESSy b/TESSy
new file mode 100644
index 0000000..745c145
--- /dev/null
+++ b/TESSy
@@ -0,0 +1,8 @@
+DateUsedForStatistics
+ReportingCountry
+Gender
+Age
+Imported
+DateOfSampling
+DateOfReceiptReferenceLab
+ProbableCountryOfInfection
diff --git a/TessyIsolate.zip b/TessyIsolate.zip
new file mode 100644
index 0000000..d387ed1
Binary files /dev/null and b/TessyIsolate.zip differ
diff --git a/bin/eu/europa/ecdc/enauploader/CsvOutputHandler.class b/bin/eu/europa/ecdc/enauploader/CsvOutputHandler.class
new file mode 100644
index 0000000..704ff21
Binary files /dev/null and b/bin/eu/europa/ecdc/enauploader/CsvOutputHandler.class differ
diff --git a/bin/eu/europa/ecdc/enauploader/ENAUtils.class b/bin/eu/europa/ecdc/enauploader/ENAUtils.class
index 11d269c..32fb3fe 100644
Binary files a/bin/eu/europa/ecdc/enauploader/ENAUtils.class and b/bin/eu/europa/ecdc/enauploader/ENAUtils.class differ
diff --git a/bin/eu/europa/ecdc/enauploader/ENAuploaderGUI$1.class b/bin/eu/europa/ecdc/enauploader/ENAuploaderGUI$1.class
new file mode 100644
index 0000000..a6da526
Binary files /dev/null and b/bin/eu/europa/ecdc/enauploader/ENAuploaderGUI$1.class differ
diff --git a/bin/eu/europa/ecdc/enauploader/ENAuploaderGUI$2.class b/bin/eu/europa/ecdc/enauploader/ENAuploaderGUI$2.class
new file mode 100644
index 0000000..f97fa4e
Binary files /dev/null and b/bin/eu/europa/ecdc/enauploader/ENAuploaderGUI$2.class differ
diff --git a/bin/eu/europa/ecdc/enauploader/ENAuploaderGUI.class b/bin/eu/europa/ecdc/enauploader/ENAuploaderGUI.class
new file mode 100644
index 0000000..e8e1100
Binary files /dev/null and b/bin/eu/europa/ecdc/enauploader/ENAuploaderGUI.class differ
diff --git a/bin/eu/europa/ecdc/enauploader/ENAuploaderMain.class b/bin/eu/europa/ecdc/enauploader/ENAuploaderMain.class
index 02a0152..905f5dc 100644
Binary files a/bin/eu/europa/ecdc/enauploader/ENAuploaderMain.class and b/bin/eu/europa/ecdc/enauploader/ENAuploaderMain.class differ
diff --git a/bin/eu/europa/ecdc/enauploader/EcdcAutomationWorker.class b/bin/eu/europa/ecdc/enauploader/EcdcAutomationWorker.class
new file mode 100644
index 0000000..a08b089
Binary files /dev/null and b/bin/eu/europa/ecdc/enauploader/EcdcAutomationWorker.class differ
diff --git a/bin/eu/europa/ecdc/enauploader/EcdcFullUploadWorker.class b/bin/eu/europa/ecdc/enauploader/EcdcFullUploadWorker.class
new file mode 100644
index 0000000..4741fe5
Binary files /dev/null and b/bin/eu/europa/ecdc/enauploader/EcdcFullUploadWorker.class differ
diff --git a/bin/eu/europa/ecdc/enauploader/EcdcImportWorker.class b/bin/eu/europa/ecdc/enauploader/EcdcImportWorker.class
new file mode 100644
index 0000000..610fbbe
Binary files /dev/null and b/bin/eu/europa/ecdc/enauploader/EcdcImportWorker.class differ
diff --git a/bin/eu/europa/ecdc/enauploader/EcdcJob.class b/bin/eu/europa/ecdc/enauploader/EcdcJob.class
new file mode 100644
index 0000000..bf87d59
Binary files /dev/null and b/bin/eu/europa/ecdc/enauploader/EcdcJob.class differ
diff --git a/bin/eu/europa/ecdc/enauploader/EcdcJobHandler.class b/bin/eu/europa/ecdc/enauploader/EcdcJobHandler.class
new file mode 100644
index 0000000..fea33ac
Binary files /dev/null and b/bin/eu/europa/ecdc/enauploader/EcdcJobHandler.class differ
diff --git a/bin/eu/europa/ecdc/enauploader/EcdcJobPanel.class b/bin/eu/europa/ecdc/enauploader/EcdcJobPanel.class
new file mode 100644
index 0000000..37fe948
Binary files /dev/null and b/bin/eu/europa/ecdc/enauploader/EcdcJobPanel.class differ
diff --git a/bin/eu/europa/ecdc/enauploader/EcdcLinkWorker.class b/bin/eu/europa/ecdc/enauploader/EcdcLinkWorker.class
new file mode 100644
index 0000000..320d937
Binary files /dev/null and b/bin/eu/europa/ecdc/enauploader/EcdcLinkWorker.class differ
diff --git a/bin/eu/europa/ecdc/enauploader/EcdcSftpUploadWorker.class b/bin/eu/europa/ecdc/enauploader/EcdcSftpUploadWorker.class
new file mode 100644
index 0000000..0786645
Binary files /dev/null and b/bin/eu/europa/ecdc/enauploader/EcdcSftpUploadWorker.class differ
diff --git a/bin/eu/europa/ecdc/enauploader/EcdcTessyApprovalWorker.class b/bin/eu/europa/ecdc/enauploader/EcdcTessyApprovalWorker.class
new file mode 100644
index 0000000..2c0756e
Binary files /dev/null and b/bin/eu/europa/ecdc/enauploader/EcdcTessyApprovalWorker.class differ
diff --git a/bin/eu/europa/ecdc/enauploader/EcdcTessyCreateAndTestWorker.class b/bin/eu/europa/ecdc/enauploader/EcdcTessyCreateAndTestWorker.class
new file mode 100644
index 0000000..a609491
Binary files /dev/null and b/bin/eu/europa/ecdc/enauploader/EcdcTessyCreateAndTestWorker.class differ
diff --git a/bin/eu/europa/ecdc/enauploader/EcdcTessyRejectWorker.class b/bin/eu/europa/ecdc/enauploader/EcdcTessyRejectWorker.class
new file mode 100644
index 0000000..5912caf
Binary files /dev/null and b/bin/eu/europa/ecdc/enauploader/EcdcTessyRejectWorker.class differ
diff --git a/bin/eu/europa/ecdc/enauploader/EcdcTessyUploadWorker.class b/bin/eu/europa/ecdc/enauploader/EcdcTessyUploadWorker.class
new file mode 100644
index 0000000..d24b19b
Binary files /dev/null and b/bin/eu/europa/ecdc/enauploader/EcdcTessyUploadWorker.class differ
diff --git a/bin/eu/europa/ecdc/enauploader/EcdcUploaderGUI$1.class b/bin/eu/europa/ecdc/enauploader/EcdcUploaderGUI$1.class
new file mode 100644
index 0000000..25cc53b
Binary files /dev/null and b/bin/eu/europa/ecdc/enauploader/EcdcUploaderGUI$1.class differ
diff --git a/bin/eu/europa/ecdc/enauploader/EcdcUploaderGUI$10$1.class b/bin/eu/europa/ecdc/enauploader/EcdcUploaderGUI$10$1.class
new file mode 100644
index 0000000..fa03cb3
Binary files /dev/null and b/bin/eu/europa/ecdc/enauploader/EcdcUploaderGUI$10$1.class differ
diff --git a/bin/eu/europa/ecdc/enauploader/EcdcUploaderGUI$10.class b/bin/eu/europa/ecdc/enauploader/EcdcUploaderGUI$10.class
new file mode 100644
index 0000000..fab918d
Binary files /dev/null and b/bin/eu/europa/ecdc/enauploader/EcdcUploaderGUI$10.class differ
diff --git a/bin/eu/europa/ecdc/enauploader/EcdcUploaderGUI$2.class b/bin/eu/europa/ecdc/enauploader/EcdcUploaderGUI$2.class
new file mode 100644
index 0000000..7701fa9
Binary files /dev/null and b/bin/eu/europa/ecdc/enauploader/EcdcUploaderGUI$2.class differ
diff --git a/bin/eu/europa/ecdc/enauploader/EcdcUploaderGUI$3.class b/bin/eu/europa/ecdc/enauploader/EcdcUploaderGUI$3.class
new file mode 100644
index 0000000..bb1be76
Binary files /dev/null and b/bin/eu/europa/ecdc/enauploader/EcdcUploaderGUI$3.class differ
diff --git a/bin/eu/europa/ecdc/enauploader/EcdcUploaderGUI$4.class b/bin/eu/europa/ecdc/enauploader/EcdcUploaderGUI$4.class
new file mode 100644
index 0000000..7599b7b
Binary files /dev/null and b/bin/eu/europa/ecdc/enauploader/EcdcUploaderGUI$4.class differ
diff --git a/bin/eu/europa/ecdc/enauploader/EcdcUploaderGUI$5$1.class b/bin/eu/europa/ecdc/enauploader/EcdcUploaderGUI$5$1.class
new file mode 100644
index 0000000..829c7fa
Binary files /dev/null and b/bin/eu/europa/ecdc/enauploader/EcdcUploaderGUI$5$1.class differ
diff --git a/bin/eu/europa/ecdc/enauploader/EcdcUploaderGUI$5.class b/bin/eu/europa/ecdc/enauploader/EcdcUploaderGUI$5.class
new file mode 100644
index 0000000..4daf6a8
Binary files /dev/null and b/bin/eu/europa/ecdc/enauploader/EcdcUploaderGUI$5.class differ
diff --git a/bin/eu/europa/ecdc/enauploader/EcdcUploaderGUI$6.class b/bin/eu/europa/ecdc/enauploader/EcdcUploaderGUI$6.class
new file mode 100644
index 0000000..07c431f
Binary files /dev/null and b/bin/eu/europa/ecdc/enauploader/EcdcUploaderGUI$6.class differ
diff --git a/bin/eu/europa/ecdc/enauploader/EcdcUploaderGUI$7.class b/bin/eu/europa/ecdc/enauploader/EcdcUploaderGUI$7.class
new file mode 100644
index 0000000..8ba344d
Binary files /dev/null and b/bin/eu/europa/ecdc/enauploader/EcdcUploaderGUI$7.class differ
diff --git a/bin/eu/europa/ecdc/enauploader/EcdcUploaderGUI$8.class b/bin/eu/europa/ecdc/enauploader/EcdcUploaderGUI$8.class
new file mode 100644
index 0000000..450bb62
Binary files /dev/null and b/bin/eu/europa/ecdc/enauploader/EcdcUploaderGUI$8.class differ
diff --git a/bin/eu/europa/ecdc/enauploader/EcdcUploaderGUI$9$1.class b/bin/eu/europa/ecdc/enauploader/EcdcUploaderGUI$9$1.class
new file mode 100644
index 0000000..0848ae3
Binary files /dev/null and b/bin/eu/europa/ecdc/enauploader/EcdcUploaderGUI$9$1.class differ
diff --git a/bin/eu/europa/ecdc/enauploader/EcdcUploaderGUI$9.class b/bin/eu/europa/ecdc/enauploader/EcdcUploaderGUI$9.class
new file mode 100644
index 0000000..aad0f5f
Binary files /dev/null and b/bin/eu/europa/ecdc/enauploader/EcdcUploaderGUI$9.class differ
diff --git a/bin/eu/europa/ecdc/enauploader/EcdcUploaderGUI.class b/bin/eu/europa/ecdc/enauploader/EcdcUploaderGUI.class
new file mode 100644
index 0000000..f28840e
Binary files /dev/null and b/bin/eu/europa/ecdc/enauploader/EcdcUploaderGUI.class differ
diff --git a/bin/eu/europa/ecdc/enauploader/EcdcUtils$1.class b/bin/eu/europa/ecdc/enauploader/EcdcUtils$1.class
new file mode 100644
index 0000000..0551c66
Binary files /dev/null and b/bin/eu/europa/ecdc/enauploader/EcdcUtils$1.class differ
diff --git a/bin/eu/europa/ecdc/enauploader/EcdcUtils$2.class b/bin/eu/europa/ecdc/enauploader/EcdcUtils$2.class
new file mode 100644
index 0000000..d2e3b6a
Binary files /dev/null and b/bin/eu/europa/ecdc/enauploader/EcdcUtils$2.class differ
diff --git a/bin/eu/europa/ecdc/enauploader/EcdcUtils.class b/bin/eu/europa/ecdc/enauploader/EcdcUtils.class
new file mode 100644
index 0000000..fd86e07
Binary files /dev/null and b/bin/eu/europa/ecdc/enauploader/EcdcUtils.class differ
diff --git a/bin/eu/europa/ecdc/enauploader/EnaSubmissionWorker.class b/bin/eu/europa/ecdc/enauploader/EnaSubmissionWorker.class
new file mode 100644
index 0000000..a970ff2
Binary files /dev/null and b/bin/eu/europa/ecdc/enauploader/EnaSubmissionWorker.class differ
diff --git a/bin/eu/europa/ecdc/enauploader/ExcelAdapter.class b/bin/eu/europa/ecdc/enauploader/ExcelAdapter.class
new file mode 100644
index 0000000..627056b
Binary files /dev/null and b/bin/eu/europa/ecdc/enauploader/ExcelAdapter.class differ
diff --git a/bin/eu/europa/ecdc/enauploader/Experiment.class b/bin/eu/europa/ecdc/enauploader/Experiment.class
index 2322dfa..e22a169 100644
Binary files a/bin/eu/europa/ecdc/enauploader/Experiment.class and b/bin/eu/europa/ecdc/enauploader/Experiment.class differ
diff --git a/bin/eu/europa/ecdc/enauploader/GhostEntryCreator.class b/bin/eu/europa/ecdc/enauploader/GhostEntryCreator.class
new file mode 100644
index 0000000..d3800cc
Binary files /dev/null and b/bin/eu/europa/ecdc/enauploader/GhostEntryCreator.class differ
diff --git a/bin/eu/europa/ecdc/enauploader/ImportConfig.class b/bin/eu/europa/ecdc/enauploader/ImportConfig.class
new file mode 100644
index 0000000..a9f2cd9
Binary files /dev/null and b/bin/eu/europa/ecdc/enauploader/ImportConfig.class differ
diff --git a/bin/eu/europa/ecdc/enauploader/ImportConfigGUI.class b/bin/eu/europa/ecdc/enauploader/ImportConfigGUI.class
new file mode 100644
index 0000000..d63cddc
Binary files /dev/null and b/bin/eu/europa/ecdc/enauploader/ImportConfigGUI.class differ
diff --git a/bin/eu/europa/ecdc/enauploader/ImportConfigHandler.class b/bin/eu/europa/ecdc/enauploader/ImportConfigHandler.class
new file mode 100644
index 0000000..b6c3056
Binary files /dev/null and b/bin/eu/europa/ecdc/enauploader/ImportConfigHandler.class differ
diff --git a/bin/eu/europa/ecdc/enauploader/ImportMapPanel$1.class b/bin/eu/europa/ecdc/enauploader/ImportMapPanel$1.class
new file mode 100644
index 0000000..7b7e113
Binary files /dev/null and b/bin/eu/europa/ecdc/enauploader/ImportMapPanel$1.class differ
diff --git a/bin/eu/europa/ecdc/enauploader/ImportMapPanel$2.class b/bin/eu/europa/ecdc/enauploader/ImportMapPanel$2.class
new file mode 100644
index 0000000..a344dcc
Binary files /dev/null and b/bin/eu/europa/ecdc/enauploader/ImportMapPanel$2.class differ
diff --git a/bin/eu/europa/ecdc/enauploader/ImportMapPanel$3.class b/bin/eu/europa/ecdc/enauploader/ImportMapPanel$3.class
new file mode 100644
index 0000000..ad19a83
Binary files /dev/null and b/bin/eu/europa/ecdc/enauploader/ImportMapPanel$3.class differ
diff --git a/bin/eu/europa/ecdc/enauploader/ImportMapPanel.class b/bin/eu/europa/ecdc/enauploader/ImportMapPanel.class
new file mode 100644
index 0000000..c2fdaad
Binary files /dev/null and b/bin/eu/europa/ecdc/enauploader/ImportMapPanel.class differ
diff --git a/bin/eu/europa/ecdc/enauploader/ImportScheduler.class b/bin/eu/europa/ecdc/enauploader/ImportScheduler.class
new file mode 100644
index 0000000..20b8aa6
Binary files /dev/null and b/bin/eu/europa/ecdc/enauploader/ImportScheduler.class differ
diff --git a/bin/eu/europa/ecdc/enauploader/ImportSqlAuth.class b/bin/eu/europa/ecdc/enauploader/ImportSqlAuth.class
new file mode 100644
index 0000000..d0df92a
Binary files /dev/null and b/bin/eu/europa/ecdc/enauploader/ImportSqlAuth.class differ
diff --git a/bin/eu/europa/ecdc/enauploader/ImportTools.class b/bin/eu/europa/ecdc/enauploader/ImportTools.class
new file mode 100644
index 0000000..17838ad
Binary files /dev/null and b/bin/eu/europa/ecdc/enauploader/ImportTools.class differ
diff --git a/bin/eu/europa/ecdc/enauploader/JCIFSEngine.class b/bin/eu/europa/ecdc/enauploader/JCIFSEngine.class
new file mode 100644
index 0000000..033ae05
Binary files /dev/null and b/bin/eu/europa/ecdc/enauploader/JCIFSEngine.class differ
diff --git a/bin/eu/europa/ecdc/enauploader/JCIFSNTLMSchemeFactory.class b/bin/eu/europa/ecdc/enauploader/JCIFSNTLMSchemeFactory.class
new file mode 100644
index 0000000..ee44a69
Binary files /dev/null and b/bin/eu/europa/ecdc/enauploader/JCIFSNTLMSchemeFactory.class differ
diff --git a/bin/eu/europa/ecdc/enauploader/JTableOutputHandler.class b/bin/eu/europa/ecdc/enauploader/JTableOutputHandler.class
new file mode 100644
index 0000000..5154036
Binary files /dev/null and b/bin/eu/europa/ecdc/enauploader/JTableOutputHandler.class differ
diff --git a/bin/eu/europa/ecdc/enauploader/JTextActionArea.class b/bin/eu/europa/ecdc/enauploader/JTextActionArea.class
new file mode 100644
index 0000000..c4d8936
Binary files /dev/null and b/bin/eu/europa/ecdc/enauploader/JTextActionArea.class differ
diff --git a/bin/eu/europa/ecdc/enauploader/OutputHandler.class b/bin/eu/europa/ecdc/enauploader/OutputHandler.class
new file mode 100644
index 0000000..abb0406
Binary files /dev/null and b/bin/eu/europa/ecdc/enauploader/OutputHandler.class differ
diff --git a/bin/eu/europa/ecdc/enauploader/Run.class b/bin/eu/europa/ecdc/enauploader/Run.class
index fab20cf..f3f58c4 100644
Binary files a/bin/eu/europa/ecdc/enauploader/Run.class and b/bin/eu/europa/ecdc/enauploader/Run.class differ
diff --git a/bin/eu/europa/ecdc/enauploader/Submission.class b/bin/eu/europa/ecdc/enauploader/Submission.class
index dfc2c98..ddc6e1a 100644
Binary files a/bin/eu/europa/ecdc/enauploader/Submission.class and b/bin/eu/europa/ecdc/enauploader/Submission.class differ
diff --git a/bin/eu/europa/ecdc/enauploader/SubmissionWorker.class b/bin/eu/europa/ecdc/enauploader/SubmissionWorker.class
new file mode 100644
index 0000000..30e4427
Binary files /dev/null and b/bin/eu/europa/ecdc/enauploader/SubmissionWorker.class differ
diff --git a/bin/eu/europa/ecdc/enauploader/TessyBatch.class b/bin/eu/europa/ecdc/enauploader/TessyBatch.class
new file mode 100644
index 0000000..0ffd90c
Binary files /dev/null and b/bin/eu/europa/ecdc/enauploader/TessyBatch.class differ
diff --git a/bin/eu/europa/ecdc/enauploader/TessyCredentials.class b/bin/eu/europa/ecdc/enauploader/TessyCredentials.class
new file mode 100644
index 0000000..0d7b9e6
Binary files /dev/null and b/bin/eu/europa/ecdc/enauploader/TessyCredentials.class differ
diff --git a/bin/eu/europa/ecdc/enauploader/TessyIsolate$1.class b/bin/eu/europa/ecdc/enauploader/TessyIsolate$1.class
new file mode 100644
index 0000000..dc835a9
Binary files /dev/null and b/bin/eu/europa/ecdc/enauploader/TessyIsolate$1.class differ
diff --git a/bin/eu/europa/ecdc/enauploader/TessyIsolate$DefaultTrustManager.class b/bin/eu/europa/ecdc/enauploader/TessyIsolate$DefaultTrustManager.class
new file mode 100644
index 0000000..3d6459c
Binary files /dev/null and b/bin/eu/europa/ecdc/enauploader/TessyIsolate$DefaultTrustManager.class differ
diff --git a/bin/eu/europa/ecdc/enauploader/TessyIsolate.class b/bin/eu/europa/ecdc/enauploader/TessyIsolate.class
new file mode 100644
index 0000000..927dfff
Binary files /dev/null and b/bin/eu/europa/ecdc/enauploader/TessyIsolate.class differ
diff --git a/bin/eu/europa/ecdc/enauploader/TessyUploader$DefaultTrustManager.class b/bin/eu/europa/ecdc/enauploader/TessyUploader$DefaultTrustManager.class
new file mode 100644
index 0000000..ca3e335
Binary files /dev/null and b/bin/eu/europa/ecdc/enauploader/TessyUploader$DefaultTrustManager.class differ
diff --git a/bin/eu/europa/ecdc/enauploader/TessyUploader.class b/bin/eu/europa/ecdc/enauploader/TessyUploader.class
new file mode 100644
index 0000000..1000be4
Binary files /dev/null and b/bin/eu/europa/ecdc/enauploader/TessyUploader.class differ
diff --git a/bin/eu/europa/ecdc/enauploader/TessyValidationResult.class b/bin/eu/europa/ecdc/enauploader/TessyValidationResult.class
new file mode 100644
index 0000000..41b452e
Binary files /dev/null and b/bin/eu/europa/ecdc/enauploader/TessyValidationResult.class differ
diff --git a/bin/eu/europa/ecdc/enauploader/UploadConfig.class b/bin/eu/europa/ecdc/enauploader/UploadConfig.class
new file mode 100644
index 0000000..7dfa864
Binary files /dev/null and b/bin/eu/europa/ecdc/enauploader/UploadConfig.class differ
diff --git a/bin/eu/europa/ecdc/enauploader/UploadConfigGUI.class b/bin/eu/europa/ecdc/enauploader/UploadConfigGUI.class
new file mode 100644
index 0000000..21bc1b2
Binary files /dev/null and b/bin/eu/europa/ecdc/enauploader/UploadConfigGUI.class differ
diff --git a/bin/eu/europa/ecdc/enauploader/UploadConfigHandler.class b/bin/eu/europa/ecdc/enauploader/UploadConfigHandler.class
new file mode 100644
index 0000000..210931a
Binary files /dev/null and b/bin/eu/europa/ecdc/enauploader/UploadConfigHandler.class differ
diff --git a/bin/eu/europa/ecdc/enauploader/media/ECDC.png b/bin/eu/europa/ecdc/enauploader/media/ECDC.png
new file mode 100644
index 0000000..d7a5918
Binary files /dev/null and b/bin/eu/europa/ecdc/enauploader/media/ECDC.png differ
diff --git a/bin/eu/europa/ecdc/enauploader/media/ECDC2.png b/bin/eu/europa/ecdc/enauploader/media/ECDC2.png
new file mode 100644
index 0000000..2bd4fd0
Binary files /dev/null and b/bin/eu/europa/ecdc/enauploader/media/ECDC2.png differ
diff --git a/checklist.txt b/checklist.txt
new file mode 100644
index 0000000..8595181
--- /dev/null
+++ b/checklist.txt
@@ -0,0 +1 @@
+ERC000028
\ No newline at end of file
diff --git a/doc/eu/europa/ecdc/enauploader/TessyBatch.html b/doc/eu/europa/ecdc/enauploader/TessyBatch.html
new file mode 100644
index 0000000..7d26a65
--- /dev/null
+++ b/doc/eu/europa/ecdc/enauploader/TessyBatch.html
@@ -0,0 +1,377 @@
+
+
+
+
+
+TessyBatch
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+JavaScript is disabled on your browser.
+
+
+
+
+
+
+
+
+
+
+
+
+JavaScript is disabled on your browser.
+
+
+
+
+Summary:
+Nested |
+Field |
+Constr |
+Method
+
+
+Detail:
+Field |
+Constr |
+Method
+
+
+
+
+
+
+
+
+
+
+
+
+
+java.lang.Object
+
+
+eu.europa.ecdc.enauploader.TessyBatch
+
+
+
+
+
+
+
+
+
+
+
+
+Method Summary
+
+
+
+
+
+Methods inherited from class java.lang.Object
+equals, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Method Detail
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+reject
+public void reject()
+
+
+
+
+
+
+
+
+
+
+
+upload
+public boolean upload()
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+JavaScript is disabled on your browser.
+
+
+
+
+Summary:
+Nested |
+Field |
+Constr |
+Method
+
+
+Detail:
+Field |
+Constr |
+Method
+
+
+
+
+
+
+
+
diff --git a/doc/eu/europa/ecdc/enauploader/TessyCredentials.html b/doc/eu/europa/ecdc/enauploader/TessyCredentials.html
new file mode 100644
index 0000000..0884d4c
--- /dev/null
+++ b/doc/eu/europa/ecdc/enauploader/TessyCredentials.html
@@ -0,0 +1,391 @@
+
+
+
+
+
+TessyCredentials
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+JavaScript is disabled on your browser.
+
+
+
+
+
+
+
+
+
+
+
+
+JavaScript is disabled on your browser.
+
+
+
+
+Summary:
+Nested |
+Field |
+Constr |
+Method
+
+
+Detail:
+Field |
+Constr |
+Method
+
+
+
+
+
+
+
+
+
+
+
+
+
+java.lang.Object
+
+
+eu.europa.ecdc.enauploader.TessyCredentials
+
+
+
+
+
+
+
+
+
+
+
+
+Method Summary
+
+
+
+
+
+Methods inherited from class java.lang.Object
+equals, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+JavaScript is disabled on your browser.
+
+
+
+
+Summary:
+Nested |
+Field |
+Constr |
+Method
+
+
+Detail:
+Field |
+Constr |
+Method
+
+
+
+
+
+
+
+
diff --git a/doc/eu/europa/ecdc/enauploader/TessyIsolate.DefaultTrustManager.html b/doc/eu/europa/ecdc/enauploader/TessyIsolate.DefaultTrustManager.html
new file mode 100644
index 0000000..fe08fff
--- /dev/null
+++ b/doc/eu/europa/ecdc/enauploader/TessyIsolate.DefaultTrustManager.html
@@ -0,0 +1,360 @@
+
+
+
+
+
+TessyIsolate.DefaultTrustManager
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+JavaScript is disabled on your browser.
+
+
+
+
+
+
+
+
+
+
+
+
+JavaScript is disabled on your browser.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+java.lang.Object
+
+
+eu.europa.ecdc.enauploader.TessyIsolate.DefaultTrustManager
+
+
+
+
+
+
+
+
+
+
+
+
+Constructor Summary
+
+
+
+
+
+
+
+
+Method Summary
+
+
+
+
+
+Methods inherited from class java.lang.Object
+equals, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Constructor Detail
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+JavaScript is disabled on your browser.
+
+
+
+
+
+
+
+
+
diff --git a/doc/eu/europa/ecdc/enauploader/TessyIsolate.html b/doc/eu/europa/ecdc/enauploader/TessyIsolate.html
new file mode 100644
index 0000000..74da5d4
--- /dev/null
+++ b/doc/eu/europa/ecdc/enauploader/TessyIsolate.html
@@ -0,0 +1,428 @@
+
+
+
+
+
+TessyIsolate
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+JavaScript is disabled on your browser.
+
+
+
+
+
+
+
+
+
+
+
+
+JavaScript is disabled on your browser.
+
+
+
+
+
+Detail:
+Field |
+Constr |
+Method
+
+
+
+
+
+
+
+
+
+
+
+
+
+java.lang.Object
+
+
+eu.europa.ecdc.enauploader.TessyIsolate
+
+
+
+
+
+
+
+
+
+
+
+
+Nested Class Summary
+
+
+
+
+
+
+
+
+Method Summary
+
+
+
+
+
+Methods inherited from class java.lang.Object
+equals, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Method Detail
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+reject
+public void reject()
+
+
+
+
+
+
+
+
+
+
+
+upload
+public boolean upload()
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+JavaScript is disabled on your browser.
+
+
+
+
+
+Detail:
+Field |
+Constr |
+Method
+
+
+
+
+
+
+
+
diff --git a/doc/eu/europa/ecdc/enauploader/TessyValidationResult.html b/doc/eu/europa/ecdc/enauploader/TessyValidationResult.html
new file mode 100644
index 0000000..93491a8
--- /dev/null
+++ b/doc/eu/europa/ecdc/enauploader/TessyValidationResult.html
@@ -0,0 +1,469 @@
+
+
+
+
+
+TessyValidationResult
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+JavaScript is disabled on your browser.
+
+
+
+
+
+
+
+
+
+
+
+
+JavaScript is disabled on your browser.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+java.lang.Object
+
+
+eu.europa.ecdc.enauploader.TessyValidationResult
+
+
+
+
+
+
+
+
+
+
+
+
+Constructor Summary
+
+Constructors
+
+Constructor
+Description
+
+
+TessyValidationResult (java.lang.String resultString)
+
+
+
+
+
+
+
+
+
+
+Method Summary
+
+
+
+
+
+Methods inherited from class java.lang.Object
+equals, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Constructor Detail
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+JavaScript is disabled on your browser.
+
+
+
+
+
+
+
+
+
diff --git a/doc/eu/europa/ecdc/enauploader/class-use/TessyBatch.html b/doc/eu/europa/ecdc/enauploader/class-use/TessyBatch.html
new file mode 100644
index 0000000..4ba8456
--- /dev/null
+++ b/doc/eu/europa/ecdc/enauploader/class-use/TessyBatch.html
@@ -0,0 +1,150 @@
+
+
+
+
+
+Uses of Class eu.europa.ecdc.enauploader.TessyBatch
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+JavaScript is disabled on your browser.
+
+
+
+
+
+
+
+
+
+
+
+
+JavaScript is disabled on your browser.
+
+
+
+
+
+
+
+
+
+
+No usage of eu.europa.ecdc.enauploader.TessyBatch
+
+
+
+
+
+
+
+
+
+JavaScript is disabled on your browser.
+
+
+
+
+
+
+
+
diff --git a/doc/eu/europa/ecdc/enauploader/class-use/TessyCredentials.html b/doc/eu/europa/ecdc/enauploader/class-use/TessyCredentials.html
new file mode 100644
index 0000000..c74dc6e
--- /dev/null
+++ b/doc/eu/europa/ecdc/enauploader/class-use/TessyCredentials.html
@@ -0,0 +1,177 @@
+
+
+
+
+
+Uses of Class eu.europa.ecdc.enauploader.TessyCredentials
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+JavaScript is disabled on your browser.
+
+
+
+
+
+
+
+
+
+
+
+
+JavaScript is disabled on your browser.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+JavaScript is disabled on your browser.
+
+
+
+
+
+
+
+
diff --git a/doc/eu/europa/ecdc/enauploader/class-use/TessyIsolate.DefaultTrustManager.html b/doc/eu/europa/ecdc/enauploader/class-use/TessyIsolate.DefaultTrustManager.html
new file mode 100644
index 0000000..0197f5a
--- /dev/null
+++ b/doc/eu/europa/ecdc/enauploader/class-use/TessyIsolate.DefaultTrustManager.html
@@ -0,0 +1,150 @@
+
+
+
+
+
+Uses of Class eu.europa.ecdc.enauploader.TessyIsolate.DefaultTrustManager
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+JavaScript is disabled on your browser.
+
+
+
+
+
+
+
+
+
+
+
+
+JavaScript is disabled on your browser.
+
+
+
+
+
+
+
+
+
+
+No usage of eu.europa.ecdc.enauploader.TessyIsolate.DefaultTrustManager
+
+
+
+
+
+
+
+
+
+JavaScript is disabled on your browser.
+
+
+
+
+
+
+
+
diff --git a/doc/eu/europa/ecdc/enauploader/class-use/TessyIsolate.html b/doc/eu/europa/ecdc/enauploader/class-use/TessyIsolate.html
new file mode 100644
index 0000000..95bb1a1
--- /dev/null
+++ b/doc/eu/europa/ecdc/enauploader/class-use/TessyIsolate.html
@@ -0,0 +1,177 @@
+
+
+
+
+
+Uses of Class eu.europa.ecdc.enauploader.TessyIsolate
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+JavaScript is disabled on your browser.
+
+
+
+
+
+
+
+
+
+
+
+
+JavaScript is disabled on your browser.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+JavaScript is disabled on your browser.
+
+
+
+
+
+
+
+
diff --git a/doc/eu/europa/ecdc/enauploader/class-use/TessyValidationResult.html b/doc/eu/europa/ecdc/enauploader/class-use/TessyValidationResult.html
new file mode 100644
index 0000000..4833c70
--- /dev/null
+++ b/doc/eu/europa/ecdc/enauploader/class-use/TessyValidationResult.html
@@ -0,0 +1,202 @@
+
+
+
+
+
+Uses of Class eu.europa.ecdc.enauploader.TessyValidationResult
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+JavaScript is disabled on your browser.
+
+
+
+
+
+
+
+
+
+
+
+
+JavaScript is disabled on your browser.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+JavaScript is disabled on your browser.
+
+
+
+
+
+
+
+
diff --git a/doc/help-doc.html b/doc/help-doc.html
new file mode 100644
index 0000000..eae10aa
--- /dev/null
+++ b/doc/help-doc.html
@@ -0,0 +1,251 @@
+
+
+
+
+
+API Help
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+JavaScript is disabled on your browser.
+
+
+
+
+
+
+
+
+
+
+
+
+JavaScript is disabled on your browser.
+
+
+
+
+
+
+
+
+
+
+
+
+
+Package
+Each package has a page that contains a list of its classes and interfaces, with a summary for each. This page can contain six categories:
+
+Interfaces (italic)
+Classes
+Enums
+Exceptions
+Errors
+Annotation Types
+
+
+
+Class/Interface
+Each class, interface, nested class and nested interface has its own separate page. Each of these pages has three sections consisting of a class/interface description, summary tables, and detailed member descriptions:
+
+Class inheritance diagram
+Direct Subclasses
+All Known Subinterfaces
+All Known Implementing Classes
+Class/interface declaration
+Class/interface description
+
+
+Nested Class Summary
+Field Summary
+Constructor Summary
+Method Summary
+
+
+Field Detail
+Constructor Detail
+Method Detail
+
+Each summary entry contains the first sentence from the detailed description for that item. The summary entries are alphabetical, while the detailed descriptions are in the order they appear in the source code. This preserves the logical groupings established by the programmer.
+
+
+Annotation Type
+Each annotation type has its own separate page with the following sections:
+
+Annotation Type declaration
+Annotation Type description
+Required Element Summary
+Optional Element Summary
+Element Detail
+
+
+
+Enum
+Each enum has its own separate page with the following sections:
+
+Enum declaration
+Enum description
+Enum Constant Summary
+Enum Constant Detail
+
+
+
+Use
+Each documented package, class and interface has its own Use page. This page describes what packages, classes, methods, constructors and fields use any part of the given class or package. Given a class or interface A, its Use page includes subclasses of A, fields declared as A, methods that return A, and methods and constructors with parameters of type A. You can access this page by first going to the package, class or interface, then clicking on the "Use" link in the navigation bar.
+
+
+Tree (Class Hierarchy)
+There is a Class Hierarchy page for all packages, plus a hierarchy for each package. Each hierarchy page contains a list of classes and a list of interfaces. The classes are organized by inheritance structure starting with java.lang.Object
. The interfaces do not inherit from java.lang.Object
.
+
+When viewing the Overview page, clicking on "Tree" displays the hierarchy for all packages.
+When viewing a particular package, class or interface page, clicking "Tree" displays the hierarchy for only that package.
+
+
+
+Deprecated API
+The Deprecated API page lists all of the API that have been deprecated. A deprecated API is not recommended for use, generally due to improvements, and a replacement API is usually given. Deprecated APIs may be removed in future implementations.
+
+
+Index
+The Index contains an alphabetic list of all classes, interfaces, constructors, methods, and fields.
+
+
+Prev/Next
+These links take you to the next or previous class, interface, package, or related page.
+
+
+Frames/No Frames
+These links show and hide the HTML frames. All pages are available with or without frames.
+
+
+All Classes
+The All Classes link shows all classes and interfaces except non-static nested types.
+
+
+Serialized Form
+Each serializable or externalizable class has a description of its serialization fields and methods. This information is of interest to re-implementors, not to developers using the API. While there is no link in the navigation bar, you can get to this information by going to any serialized class and clicking "Serialized Form" in the "See also" section of the class description.
+
+
+Constant Field Values
+The Constant Field Values page lists the static final fields and their values.
+
+
+
This help file applies to API documentation generated using the standard doclet.
+
+
+
+
+
+
+
+
+
+JavaScript is disabled on your browser.
+
+
+
+
+
+
+
+
diff --git a/doc/resources/glass.png b/doc/resources/glass.png
new file mode 100644
index 0000000..a7f591f
Binary files /dev/null and b/doc/resources/glass.png differ
diff --git a/lastopen.txt b/lastopen.txt
new file mode 100644
index 0000000..fc480e0
--- /dev/null
+++ b/lastopen.txt
@@ -0,0 +1 @@
+C:\Users\ealm\Desktop\tessy3
\ No newline at end of file
diff --git a/metadata.txt b/metadata.txt
new file mode 100644
index 0000000..0dfef45
--- /dev/null
+++ b/metadata.txt
@@ -0,0 +1,12 @@
+DateUsedForStatistics;collection_date
+ReportingCountry;geographic location (country and/or sea)
+Gender;
+Age;
+Imported;
+ProbableCountryOfInfection;
+Serotype;
+Specimen;
+SampleOrigin;
+DateOfSampling;
+DateOfReceiptReferenceLab;
+WgsAssembler;
\ No newline at end of file
diff --git a/paths.txt b/paths.txt
new file mode 100644
index 0000000..ad4753f
--- /dev/null
+++ b/paths.txt
@@ -0,0 +1,3 @@
+CURL=T:/Epidemiological Methods/MolecularSurveillance/Software/curl.exe
+TMP=C:/ENAtmp
+FTP=webin.ebi.ac.uk
\ No newline at end of file
diff --git a/sample.in.csv b/sample.in.csv
new file mode 100644
index 0000000..a924788
--- /dev/null
+++ b/sample.in.csv
@@ -0,0 +1,140 @@
+id,file_pattern,instrument,species,collected_by,collection_date,country,1,2,3,4,5
+B1BFCD74-60B8-41BB-9988-61FEB4A33D83,B1BFCD74-60B8-41BB-9988-61FEB4A33D83,Illumina NextSeq 550,Listeria monocytogenes,Austria,18/01/2010,Austria,,,,,
+CB5B6286-DD84-441A-933A-EE2AAF6FC21C,CB5B6286-DD84-441A-933A-EE2AAF6FC21C,Illumina NextSeq 550,Listeria monocytogenes,Austria,26/01/2010,Austria,,,,,
+E19A51D8-6A81-4391-A17F-D771C3503676,E19A51D8-6A81-4391-A17F-D771C3503676,Illumina NextSeq 550,Listeria monocytogenes,Austria,26/01/2010,Austria,,,,,
+E89B0641-3C7F-440F-ADDB-23F8DEC32B50,E89B0641-3C7F-440F-ADDB-23F8DEC32B50,Illumina MiSeq,Listeria monocytogenes,Austria,05/02/2010,Austria,,,,,
+009F450F-EA69-42B5-B7B7-344CF5FAE01F,009F450F-EA69-42B5-B7B7-344CF5FAE01F,Illumina HiSeq 2000,Listeria monocytogenes,Austria,05/02/2010,Austria,,,,,
+8891D44C-A026-4E4C-A818-BE541320A354,8891D44C-A026-4E4C-A818-BE541320A354,Illumina NextSeq 550,Listeria monocytogenes,Austria,17/02/2010,Austria,,,,,
+B45681E3-4E89-4C7C-9828-A8C5AA0B0E7C,B45681E3-4E89-4C7C-9828-A8C5AA0B0E7C,Illumina NextSeq 550,Listeria monocytogenes,Austria,25/02/2010,Austria,,,,,
+FA250076-BF3A-4F90-BDD6-5003C9CD9168,FA250076-BF3A-4F90-BDD6-5003C9CD9168,Illumina NextSeq 550,Listeria monocytogenes,Austria,15/03/2010,Austria,,,,,
+6EB56B0C-6FB1-4C9B-8C9F-B1F2850C84D2,6EB56B0C-6FB1-4C9B-8C9F-B1F2850C84D2,Illumina NextSeq 550,Listeria monocytogenes,Austria,15/03/2010,Austria,,,,,
+5E854261-0573-4A15-A318-AA0E2C66F3B1,5E854261-0573-4A15-A318-AA0E2C66F3B1,Illumina NextSeq 550,Listeria monocytogenes,Austria,15/03/2010,Austria,,,,,
+53B74A09-0BB6-4730-A64E-5CC162E437BA,53B74A09-0BB6-4730-A64E-5CC162E437BA,Illumina HiSeq 2000,Listeria monocytogenes,Austria,23/03/2010,Austria,,,,,
+6FC7B5CC-C4ED-45B7-8FC7-73E7974D6FA1,6FC7B5CC-C4ED-45B7-8FC7-73E7974D6FA1,Illumina HiSeq 2000,Listeria monocytogenes,Austria,10/04/2010,Austria,,,,,
+18BC1D3D-EF6C-4E59-A6A0-D4B757E1DE7F,18BC1D3D-EF6C-4E59-A6A0-D4B757E1DE7F,Illumina HiSeq 2000,Listeria monocytogenes,Austria,19/04/2010,Austria,,,,,
+14825576-3EF4-4ABA-BC18-9CF71A5CA589,14825576-3EF4-4ABA-BC18-9CF71A5CA589,Illumina NextSeq 550,Listeria monocytogenes,Austria,23/04/2010,Austria,,,,,
+4B4EF77D-C618-4566-BB4F-0CAA983934E7,4B4EF77D-C618-4566-BB4F-0CAA983934E7,Illumina HiSeq 2000,Listeria monocytogenes,Austria,27/04/2010,Austria,,,,,
+C1AB0BA8-1E17-4666-B48D-E7BCB4FDE873,C1AB0BA8-1E17-4666-B48D-E7BCB4FDE873,Illumina NextSeq 550,Listeria monocytogenes,Austria,06/05/2010,Austria,,,,,
+D090382E-86F5-49AD-AC99-BCBDD8DC9242,D090382E-86F5-49AD-AC99-BCBDD8DC9242,Illumina NextSeq 550,Listeria monocytogenes,Austria,13/05/2010,Austria,,,,,
+10717E34-B073-4726-B511-906BD4ED60E5,10717E34-B073-4726-B511-906BD4ED60E5,Illumina HiSeq 2000,Listeria monocytogenes,Austria,14/05/2010,Austria,,,,,
+0792E205-95C3-4072-9131-D00670E39C9A,0792E205-95C3-4072-9131-D00670E39C9A,Illumina NextSeq 550,Listeria monocytogenes,Austria,28/05/2010,Austria,,,,,
+9F2A84BB-D562-4718-87C4-76C87AFBDCBD,9F2A84BB-D562-4718-87C4-76C87AFBDCBD,Illumina NextSeq 550,Listeria monocytogenes,Austria,30/05/2010,Austria,,,,,
+D53B03B9-C032-42C3-9149-BA4F795385C4,D53B03B9-C032-42C3-9149-BA4F795385C4,Illumina NextSeq 550,Listeria monocytogenes,Austria,01/07/2010,Austria,,,,,
+3CCE04CB-0AC8-41F2-A6FE-671A74732687,3CCE04CB-0AC8-41F2-A6FE-671A74732687,Illumina NextSeq 550,Listeria monocytogenes,Austria,01/07/2010,Austria,,,,,
+887AA976-2EE8-41E3-B2A6-413CC6A46BF5,887AA976-2EE8-41E3-B2A6-413CC6A46BF5,Illumina NextSeq 550,Listeria monocytogenes,Austria,01/07/2010,Austria,,,,,
+45E1BBA7-8D62-4135-B7F6-6170030DCFFD,45E1BBA7-8D62-4135-B7F6-6170030DCFFD,Illumina NextSeq 550,Listeria monocytogenes,Austria,01/07/2010,Austria,,,,,
+FE4A0190-FD90-4363-8B1A-890E661B5445,FE4A0190-FD90-4363-8B1A-890E661B5445,Illumina NextSeq 550,Listeria monocytogenes,Austria,01/07/2010,Austria,,,,,
+252AA534-190B-477E-9FC1-593241DA69E6,252AA534-190B-477E-9FC1-593241DA69E6,Illumina NextSeq 550,Listeria monocytogenes,Austria,23/08/2010,Austria,,,,,
+875245B5-58B5-412D-ACC3-45C92B1C82CB,875245B5-58B5-412D-ACC3-45C92B1C82CB,Illumina NextSeq 550,Listeria monocytogenes,Austria,15/11/2010,Austria,,,,,
+55D8311D-28E8-40D5-AE13-084D39CF4231,55D8311D-28E8-40D5-AE13-084D39CF4231,Illumina HiSeq 2000,Listeria monocytogenes,Austria,10/12/2010,Austria,,,,,
+E77E911E-0678-478E-AE67-D602D2899925,E77E911E-0678-478E-AE67-D602D2899925,Illumina NextSeq 550,Listeria monocytogenes,Austria,29/12/2010,Austria,,,,,
+9DC4B2F5-6210-48EA-938E-A51375B0A242,9DC4B2F5-6210-48EA-938E-A51375B0A242,Illumina NextSeq 550,Listeria monocytogenes,Austria,03/01/2011,Austria,,,,,
+5175694E-A9AA-432F-8D79-BC87BA07D1F6,5175694E-A9AA-432F-8D79-BC87BA07D1F6,Illumina HiSeq 2000,Listeria monocytogenes,Austria,26/01/2011,Austria,,,,,
+3E31F383-F0B9-439F-8EDB-66236C6A7512,3E31F383-F0B9-439F-8EDB-66236C6A7512,Illumina NextSeq 550,Listeria monocytogenes,Austria,03/03/2011,Austria,,,,,
+BA20DCF9-F16D-41F2-BB4A-98BA54750FB4,BA20DCF9-F16D-41F2-BB4A-98BA54750FB4,Illumina HiSeq 2000,Listeria monocytogenes,Austria,13/03/2011,Austria,,,,,
+300262CE-FBA0-489F-B76A-81C35C0DFA0D,300262CE-FBA0-489F-B76A-81C35C0DFA0D,Illumina HiSeq 2000,Listeria monocytogenes,Austria,18/03/2011,Austria,,,,,
+B3C67105-81F6-4654-ABBC-713F101379AA,B3C67105-81F6-4654-ABBC-713F101379AA,Illumina NextSeq 550,Listeria monocytogenes,Austria,07/04/2011,Austria,,,,,
+05461485-E218-423F-A587-0840F5B46EAE,05461485-E218-423F-A587-0840F5B46EAE,Illumina NextSeq 550,Listeria monocytogenes,Austria,12/04/2011,Austria,,,,,
+7FAB000F-367D-4910-8C24-AD3B2E83E955,7FAB000F-367D-4910-8C24-AD3B2E83E955,Illumina HiSeq 2000,Listeria monocytogenes,Austria,16/04/2011,Austria,,,,,
+1B7CE6B3-7EA3-4D5F-8FCF-35A322C3628E,1B7CE6B3-7EA3-4D5F-8FCF-35A322C3628E,Illumina NextSeq 550,Listeria monocytogenes,Austria,10/05/2011,Austria,,,,,
+ACABE62E-835D-48E5-A2CC-E20AA740524E,ACABE62E-835D-48E5-A2CC-E20AA740524E,Illumina HiSeq 2000,Listeria monocytogenes,Austria,14/06/2011,Austria,,,,,
+CAD11CEE-DC1F-4254-8321-78A211CD4D0C,CAD11CEE-DC1F-4254-8321-78A211CD4D0C,Illumina NextSeq 550,Listeria monocytogenes,Austria,18/06/2011,Austria,,,,,
+3BA24473-9BA2-4A93-B300-67C41757B985,3BA24473-9BA2-4A93-B300-67C41757B985,Illumina NextSeq 550,Listeria monocytogenes,Austria,01/07/2011,Austria,,,,,
+45EB4B3B-B206-4861-B7AC-CC3369CDE662,45EB4B3B-B206-4861-B7AC-CC3369CDE662,Illumina HiSeq 2000,Listeria monocytogenes,Austria,11/07/2011,Austria,,,,,
+09BE7F2E-A0BC-4548-97D1-AB292EA8B0E0,09BE7F2E-A0BC-4548-97D1-AB292EA8B0E0,Illumina NextSeq 550,Listeria monocytogenes,Austria,08/08/2011,Austria,,,,,
+6F8101B4-1964-4EE7-939E-29733905F04D,6F8101B4-1964-4EE7-939E-29733905F04D,Illumina HiSeq 2000,Listeria monocytogenes,Austria,22/08/2011,Austria,,,,,
+8F87D747-8BCE-4C52-AAD0-6E7EB548BC4C,8F87D747-8BCE-4C52-AAD0-6E7EB548BC4C,Illumina HiSeq 2000,Listeria monocytogenes,Austria,07/10/2011,Austria,,,,,
+0FDDBDF7-D9DB-464E-8ABE-D4B2A5799368,0FDDBDF7-D9DB-464E-8ABE-D4B2A5799368,Illumina NextSeq 550,Listeria monocytogenes,Austria,07/10/2011,Austria,,,,,
+95CC9D05-1FCB-43F2-AA2E-882F53557F01,95CC9D05-1FCB-43F2-AA2E-882F53557F01,Illumina NextSeq 550,Listeria monocytogenes,Austria,14/10/2011,Austria,,,,,
+95495911-C7DB-4799-90FD-A5AB9539AA1C,95495911-C7DB-4799-90FD-A5AB9539AA1C,Illumina HiSeq 2000,Listeria monocytogenes,Austria,25/10/2011,Austria,,,,,
+E3A654F5-D1FE-4303-A234-19A9A5548753,E3A654F5-D1FE-4303-A234-19A9A5548753,Illumina NextSeq 550,Listeria monocytogenes,Austria,10/11/2011,Austria,,,,,
+642CAEB8-BC89-4919-8ABC-187FEA5CA8AB,642CAEB8-BC89-4919-8ABC-187FEA5CA8AB,Illumina NextSeq 550,Listeria monocytogenes,Austria,15/12/2011,Austria,,,,,
+C68720E8-A59F-4795-B0E1-7E3C044F686F,C68720E8-A59F-4795-B0E1-7E3C044F686F,Illumina MiSeq,Listeria monocytogenes,Austria,18/01/2013,Austria,,,,,
+C9BDF4AF-141B-4F47-AC73-6E6FF3FF1893,C9BDF4AF-141B-4F47-AC73-6E6FF3FF1893,Illumina MiSeq,Listeria monocytogenes,Austria,31/01/2013,Austria,,,,,
+64668ED5-E074-4377-9BAA-3FBDFE188C49,64668ED5-E074-4377-9BAA-3FBDFE188C49,Illumina MiSeq,Listeria monocytogenes,Austria,13/03/2013,Austria,,,,,
+31B71572-BA3C-4421-B651-5E936C2C39FC,31B71572-BA3C-4421-B651-5E936C2C39FC,Illumina MiSeq,Listeria monocytogenes,Austria,17/03/2013,Austria,,,,,
+D7BE5AA5-64F6-4F31-8783-48CA023794F9,D7BE5AA5-64F6-4F31-8783-48CA023794F9,Illumina MiSeq,Listeria monocytogenes,Austria,04/04/2013,Austria,,,,,
+4718EC46-8CBF-4ED8-8A4D-BF80C71D1131,4718EC46-8CBF-4ED8-8A4D-BF80C71D1131,Illumina MiSeq,Listeria monocytogenes,Austria,16/04/2013,Austria,,,,,
+67889884-1FFC-400D-9778-0ACB8461AD3B,67889884-1FFC-400D-9778-0ACB8461AD3B,Illumina MiSeq,Listeria monocytogenes,Austria,08/05/2013,Austria,,,,,
+8FFAA0BB-EE79-44AA-9800-8773F0E4206B,8FFAA0BB-EE79-44AA-9800-8773F0E4206B,Illumina MiSeq,Listeria monocytogenes,Austria,16/05/2013,Austria,,,,,
+3547F1D7-42C6-4D50-906E-8F14E057EC7B,3547F1D7-42C6-4D50-906E-8F14E057EC7B,Illumina MiSeq,Listeria monocytogenes,Austria,22/05/2013,Austria,,,,,
+3D99AEDA-E4FA-46C6-9E32-707646538BB4,3D99AEDA-E4FA-46C6-9E32-707646538BB4,Illumina MiSeq,Listeria monocytogenes,Austria,06/06/2013,Austria,,,,,
+862E5D84-49AA-4FDB-A6E2-3BEC78E7E737,862E5D84-49AA-4FDB-A6E2-3BEC78E7E737,Illumina MiSeq,Listeria monocytogenes,Austria,12/10/2013,Austria,,,,,
+59032E81-EFA4-4594-9BE2-63E06B124B9C,59032E81-EFA4-4594-9BE2-63E06B124B9C,Illumina MiSeq,Listeria monocytogenes,Austria,12/10/2013,Austria,,,,,
+8FFF3A8B-4B47-4BE0-B515-7A7C416EC5AB,8FFF3A8B-4B47-4BE0-B515-7A7C416EC5AB,Illumina MiSeq,Listeria monocytogenes,Austria,12/10/2013,Austria,,,,,
+59326EA9-0B64-407F-A4CA-8CA6C15FE429,59326EA9-0B64-407F-A4CA-8CA6C15FE429,Illumina NextSeq 550,Listeria monocytogenes,Austria,25/10/2013,Austria,,,,,
+399B4AFD-019F-423E-B1D7-E7F400ABA4DD,399B4AFD-019F-423E-B1D7-E7F400ABA4DD,Illumina MiSeq,Listeria monocytogenes,Austria,28/10/2013,Austria,,,,,
+D84F8663-1B98-425E-8911-F93D84854E63,D84F8663-1B98-425E-8911-F93D84854E63,Illumina NextSeq 550,Listeria monocytogenes,Austria,22/11/2013,Austria,,,,,
+2AE3D543-90AC-4EEB-A16A-5934A8B728A0,2AE3D543-90AC-4EEB-A16A-5934A8B728A0,Illumina MiSeq,Listeria monocytogenes,Austria,03/01/2014,Austria,,,,,
+3F7E729F-79A1-4537-A084-F7139515D5EC,3F7E729F-79A1-4537-A084-F7139515D5EC,Illumina MiSeq,Listeria monocytogenes,Austria,29/01/2014,Austria,,,,,
+E2C6ECAB-2A46-4BF7-92B2-8FFEDCBB803C,E2C6ECAB-2A46-4BF7-92B2-8FFEDCBB803C,Illumina NextSeq 550,Listeria monocytogenes,Austria,14/03/2014,Austria,,,,,
+9B4E454A-4096-4FD1-8726-7C651FE4800D,9B4E454A-4096-4FD1-8726-7C651FE4800D,Illumina NextSeq 550,Listeria monocytogenes,Austria,28/03/2014,Austria,,,,,
+50CEF4DC-23AF-4A5B-817C-1B966D6D2531,50CEF4DC-23AF-4A5B-817C-1B966D6D2531,Illumina MiSeq,Listeria monocytogenes,Austria,31/03/2014,Austria,,,,,
+CBE56C3B-ACB7-464C-B5F8-9E29A5475ECD,CBE56C3B-ACB7-464C-B5F8-9E29A5475ECD,Illumina MiSeq,Listeria monocytogenes,Austria,30/04/2014,Austria,,,,,
+BC7FB72A-DDDF-46BB-876F-20D51669354E,BC7FB72A-DDDF-46BB-876F-20D51669354E,Illumina MiSeq,Listeria monocytogenes,Austria,09/05/2014,Austria,,,,,
+5288E47A-8615-4A96-9233-BD24FEA741A9,5288E47A-8615-4A96-9233-BD24FEA741A9,Illumina MiSeq,Listeria monocytogenes,Austria,15/05/2014,Austria,,,,,
+D2EF7A6C-1702-4C94-AC43-D8D397CF80A6,D2EF7A6C-1702-4C94-AC43-D8D397CF80A6,Illumina MiSeq,Listeria monocytogenes,Austria,12/06/2014,Austria,,,,,
+252B8B9B-3BDF-4A82-BA1F-85CCF44C35E7,252B8B9B-3BDF-4A82-BA1F-85CCF44C35E7,Illumina MiSeq,Listeria monocytogenes,Austria,12/06/2014,Austria,,,,,
+DCC6A88B-2B39-4FAA-A2FD-674BCB4D0280,DCC6A88B-2B39-4FAA-A2FD-674BCB4D0280,Illumina MiSeq,Listeria monocytogenes,Austria,12/06/2014,Austria,,,,,
+4ABEA595-AD7A-4BDB-AE1F-1ADE2796ED04,4ABEA595-AD7A-4BDB-AE1F-1ADE2796ED04,Illumina MiSeq,Listeria monocytogenes,Austria,23/06/2014,Austria,,,,,
+F1F87225-12D4-44D1-AFE0-5DAB4F6F05A3,F1F87225-12D4-44D1-AFE0-5DAB4F6F05A3,Illumina MiSeq,Listeria monocytogenes,Austria,27/06/2014,Austria,,,,,
+FFB30397-8C4E-4FA7-8D59-9A9C7B7677E9,FFB30397-8C4E-4FA7-8D59-9A9C7B7677E9,Illumina MiSeq,Listeria monocytogenes,Austria,30/06/2014,Austria,,,,,
+64F02804-A584-47A8-B11D-46DB4E60F88F,64F02804-A584-47A8-B11D-46DB4E60F88F,Illumina MiSeq,Listeria monocytogenes,Austria,01/07/2014,Austria,,,,,
+860D607B-0952-4E8B-8A78-52CD63068187,860D607B-0952-4E8B-8A78-52CD63068187,Illumina MiSeq,Listeria monocytogenes,Austria,07/07/2014,Austria,,,,,
+F1CA38CD-7E23-443A-A8F9-E9E736E65436,F1CA38CD-7E23-443A-A8F9-E9E736E65436,Illumina MiSeq,Listeria monocytogenes,Austria,07/07/2014,Austria,,,,,
+A287732B-2011-490C-BEA9-C3A32B42F740,A287732B-2011-490C-BEA9-C3A32B42F740,Illumina MiSeq,Listeria monocytogenes,Austria,15/07/2014,Austria,,,,,
+7AABE011-C6D4-4A94-8D7A-D790B187057B,7AABE011-C6D4-4A94-8D7A-D790B187057B,Illumina MiSeq,Listeria monocytogenes,Austria,18/07/2014,Austria,,,,,
+1144AB4F-8EA0-4434-BCF8-10B75F1948C2,1144AB4F-8EA0-4434-BCF8-10B75F1948C2,Illumina MiSeq,Listeria monocytogenes,Austria,28/07/2014,Austria,,,,,
+9C05F85B-3A86-4AB0-8E0B-A3648EFBD6FB,9C05F85B-3A86-4AB0-8E0B-A3648EFBD6FB,Illumina MiSeq,Listeria monocytogenes,Austria,29/07/2014,Austria,,,,,
+393DCC56-58F3-4644-97F6-068F5181DD9C,393DCC56-58F3-4644-97F6-068F5181DD9C,Illumina MiSeq,Listeria monocytogenes,Austria,08/08/2014,Austria,,,,,
+EC27B28F-C1EC-4637-BD63-F39063DCC200,EC27B28F-C1EC-4637-BD63-F39063DCC200,Illumina MiSeq,Listeria monocytogenes,Austria,11/08/2014,Austria,,,,,
+6C1BF84F-466A-48BD-B840-8EB88034C9D0,6C1BF84F-466A-48BD-B840-8EB88034C9D0,Illumina MiSeq,Listeria monocytogenes,Austria,20/08/2014,Austria,,,,,
+53DBA1F6-5921-43AB-BADC-398E91290C12,53DBA1F6-5921-43AB-BADC-398E91290C12,Illumina MiSeq,Listeria monocytogenes,Austria,22/08/2014,Austria,,,,,
+546C65FD-572E-413D-847F-6936DAEDE461,546C65FD-572E-413D-847F-6936DAEDE461,Illumina MiSeq,Listeria monocytogenes,Austria,03/09/2014,Austria,,,,,
+905DAC44-777A-4235-94CA-C2F5E5CBADDC,905DAC44-777A-4235-94CA-C2F5E5CBADDC,Illumina MiSeq,Listeria monocytogenes,Austria,08/09/2014,Austria,,,,,
+64605B7C-62FF-4A00-9CF4-F5B1F871933B,64605B7C-62FF-4A00-9CF4-F5B1F871933B,Illumina MiSeq,Listeria monocytogenes,Austria,22/09/2014,Austria,,,,,
+69D71F77-8C9C-4E1C-9DE9-A3BC8E26A466,69D71F77-8C9C-4E1C-9DE9-A3BC8E26A466,Illumina MiSeq,Listeria monocytogenes,Austria,26/09/2014,Austria,,,,,
+3F70F42C-C9FC-4194-92B9-151834C12D79,3F70F42C-C9FC-4194-92B9-151834C12D79,Illumina MiSeq,Listeria monocytogenes,Austria,13/10/2014,Austria,,,,,
+5FE3F622-A816-4F1E-85DC-0417DC3D9623,5FE3F622-A816-4F1E-85DC-0417DC3D9623,Illumina MiSeq,Listeria monocytogenes,Austria,27/10/2014,Austria,,,,,
+0B456E7B-A5C4-481B-AAEC-37929D5A8206,0B456E7B-A5C4-481B-AAEC-37929D5A8206,Illumina MiSeq,Listeria monocytogenes,Austria,28/10/2014,Austria,,,,,
+A86C2FFD-98A6-406B-881A-02E6E7B0F0A9,A86C2FFD-98A6-406B-881A-02E6E7B0F0A9,Illumina MiSeq,Listeria monocytogenes,Austria,30/10/2014,Austria,,,,,
+D9F5112B-446E-427F-91AB-A75E52FEFB32,D9F5112B-446E-427F-91AB-A75E52FEFB32,Illumina MiSeq,Listeria monocytogenes,Austria,11/11/2014,Austria,,,,,
+161999BB-FDDA-4D1E-9FB1-0C26D9753E6D,161999BB-FDDA-4D1E-9FB1-0C26D9753E6D,Illumina MiSeq,Listeria monocytogenes,Austria,25/11/2014,Austria,,,,,
+CC71061B-CA2B-4BF1-B339-D6E84A2FF0EC,CC71061B-CA2B-4BF1-B339-D6E84A2FF0EC,Illumina MiSeq,Listeria monocytogenes,Austria,04/12/2014,Austria,,,,,
+09766904-02E3-4C65-AF89-8ABE5B765B50,09766904-02E3-4C65-AF89-8ABE5B765B50,Illumina MiSeq,Listeria monocytogenes,Austria,04/12/2014,Austria,,,,,
+42C176C3-62B4-451E-A81A-93C8E4BDEDC9,42C176C3-62B4-451E-A81A-93C8E4BDEDC9,Illumina MiSeq,Listeria monocytogenes,Austria,05/12/2014,Austria,,,,,
+67E948F7-668B-428C-9FE1-9E8A96CC241C,67E948F7-668B-428C-9FE1-9E8A96CC241C,Illumina MiSeq,Listeria monocytogenes,Austria,10/12/2014,Austria,,,,,
+A48844A6-1BB5-4A8E-BB0D-FAA5B5264274,A48844A6-1BB5-4A8E-BB0D-FAA5B5264274,Illumina MiSeq,Listeria monocytogenes,Austria,16/12/2014,Austria,,,,,
+4BB96507-2C85-4C5A-95EA-284849E64DB9,4BB96507-2C85-4C5A-95EA-284849E64DB9,Illumina MiSeq,Listeria monocytogenes,Austria,29/12/2014,Austria,,,,,
+A66709D5-4B9A-409E-AE88-819816B9D0A4,A66709D5-4B9A-409E-AE88-819816B9D0A4,Illumina MiSeq,Listeria monocytogenes,Austria,30/12/2014,Austria,,,,,
+F904B314-F81C-42D5-B02A-B1AC3727D59B,F904B314-F81C-42D5-B02A-B1AC3727D59B,Illumina MiSeq,Listeria monocytogenes,Austria,07/01/2015,Austria,,,,,
+5D4CC17C-DE65-4583-A595-A42480965541,5D4CC17C-DE65-4583-A595-A42480965541,Illumina MiSeq,Listeria monocytogenes,Austria,22/01/2015,Austria,,,,,
+384570B4-7750-4FD7-8874-29F9AA989A1E,384570B4-7750-4FD7-8874-29F9AA989A1E,Illumina MiSeq,Listeria monocytogenes,Austria,22/01/2015,Austria,,,,,
+58A68E0C-3129-4078-8747-3709642E2900,58A68E0C-3129-4078-8747-3709642E2900,Illumina MiSeq,Listeria monocytogenes,Austria,22/01/2015,Austria,,,,,
+6C668BFD-ECE7-4E6C-8163-55D66CE78879,6C668BFD-ECE7-4E6C-8163-55D66CE78879,Illumina MiSeq,Listeria monocytogenes,Austria,22/01/2015,Austria,,,,,
+608F0E59-1F11-446A-B38C-927C1AAD7F50,608F0E59-1F11-446A-B38C-927C1AAD7F50,Illumina MiSeq,Listeria monocytogenes,Austria,30/01/2015,Austria,,,,,
+7B471D71-6B2A-43C3-8CA2-3300A6A4500F,7B471D71-6B2A-43C3-8CA2-3300A6A4500F,Illumina MiSeq,Listeria monocytogenes,Austria,30/01/2015,Austria,,,,,
+136748F3-C8BE-490C-8095-5B0EA23C7D85,136748F3-C8BE-490C-8095-5B0EA23C7D85,Illumina MiSeq,Listeria monocytogenes,Austria,20/02/2015,Austria,,,,,
+43611BE5-EF2B-420D-8CF0-B668D908CE5A,43611BE5-EF2B-420D-8CF0-B668D908CE5A,Illumina MiSeq,Listeria monocytogenes,Austria,23/02/2015,Austria,,,,,
+CD16ACBF-4207-400F-B338-5FA34D8FDA44,CD16ACBF-4207-400F-B338-5FA34D8FDA44,Illumina MiSeq,Listeria monocytogenes,Austria,03/03/2015,Austria,,,,,
+8C0289E1-56E1-47A5-BFEB-889D38C7B3A4,8C0289E1-56E1-47A5-BFEB-889D38C7B3A4,Illumina MiSeq,Listeria monocytogenes,Austria,21/04/2015,Austria,,,,,
+D0644D89-B59E-4E33-8606-AC7BF4211898,D0644D89-B59E-4E33-8606-AC7BF4211898,Illumina MiSeq,Listeria monocytogenes,Austria,22/04/2015,Austria,,,,,
+D8508F9A-1A42-4502-976B-ACC88BD1F57C,D8508F9A-1A42-4502-976B-ACC88BD1F57C,Illumina MiSeq,Listeria monocytogenes,Austria,28/05/2015,Austria,,,,,
+E10D5545-6CF7-4125-8AE2-B28DF065BD3D,E10D5545-6CF7-4125-8AE2-B28DF065BD3D,Illumina MiSeq,Listeria monocytogenes,Austria,02/06/2015,Austria,,,,,
+837B2062-40F3-4DDE-8CC7-D9C477E4F993,837B2062-40F3-4DDE-8CC7-D9C477E4F993,Illumina MiSeq,Listeria monocytogenes,Austria,08/06/2015,Austria,,,,,
+C1FF3D42-F1A0-4233-BE67-CA6893984042,C1FF3D42-F1A0-4233-BE67-CA6893984042,Illumina MiSeq,Listeria monocytogenes,Austria,12/06/2015,Austria,,,,,
+37369132-E105-4EA8-9576-64F740F6B136,37369132-E105-4EA8-9576-64F740F6B136,Illumina MiSeq,Listeria monocytogenes,Austria,07/07/2015,Austria,,,,,
+567563D5-9450-4ACC-BCF1-C8F54B958E55,567563D5-9450-4ACC-BCF1-C8F54B958E55,Illumina MiSeq,Listeria monocytogenes,Austria,21/07/2015,Austria,,,,,
+9BBCE9EB-4B06-48AF-96E7-92C1FF86AE6E,9BBCE9EB-4B06-48AF-96E7-92C1FF86AE6E,Illumina MiSeq,Listeria monocytogenes,Austria,07/08/2015,Austria,,,,,
+483A5956-8495-4202-818F-EA8A7C39692E,483A5956-8495-4202-818F-EA8A7C39692E,Illumina MiSeq,Listeria monocytogenes,Austria,17/08/2015,Austria,,,,,
+1B3616B7-C033-4796-A7D7-3A34C702A206,1B3616B7-C033-4796-A7D7-3A34C702A206,Illumina MiSeq,Listeria monocytogenes,Austria,25/08/2015,Austria,,,,,
+95355497-7474-4A2B-96E6-BAA4A8717443,95355497-7474-4A2B-96E6-BAA4A8717443,Illumina MiSeq,Listeria monocytogenes,Austria,01/09/2015,Austria,,,,,
+9905E1DA-7950-4D96-A695-5CEA932CE2E4,9905E1DA-7950-4D96-A695-5CEA932CE2E4,Illumina MiSeq,Listeria monocytogenes,Austria,01/10/2015,Austria,,,,,
+A5399BA3-FF72-4763-B1D1-EEE16D13AF75,A5399BA3-FF72-4763-B1D1-EEE16D13AF75,Illumina MiSeq,Listeria monocytogenes,Austria,07/10/2015,Austria,,,,,
+350F3BE8-6262-4DA3-8323-30A67358D1A8,350F3BE8-6262-4DA3-8323-30A67358D1A8,Illumina MiSeq,Listeria monocytogenes,Austria,13/10/2015,Austria,,,,,
+7B1456F4-F37F-4C4F-A579-D496C4595AC1,7B1456F4-F37F-4C4F-A579-D496C4595AC1,Illumina MiSeq,Listeria monocytogenes,Austria,10/11/2015,Austria,,,,,
+E74F40CC-9E23-45C8-A45F-F06D205ECA51,E74F40CC-9E23-45C8-A45F-F06D205ECA51,Illumina MiSeq,Listeria monocytogenes,Austria,11/11/2015,Austria,,,,,
+7DCD32E0-1B15-49FB-95A1-9D2ABDB6876F,7DCD32E0-1B15-49FB-95A1-9D2ABDB6876F,Illumina MiSeq,Listeria monocytogenes,Austria,13/11/2015,Austria,,,,,
+391F1B63-EA59-4DC8-91B0-F70CF23182B4,391F1B63-EA59-4DC8-91B0-F70CF23182B4,Illumina MiSeq,Listeria monocytogenes,Austria,20/11/2015,Austria,,,,,
+8EBC86BE-F39A-43D5-B365-612EF6287538,8EBC86BE-F39A-43D5-B365-612EF6287538,Illumina MiSeq,Listeria monocytogenes,Austria,25/11/2015,Austria,,,,,
+A1308EFC-1CA5-4C38-981E-483FD3182E91,A1308EFC-1CA5-4C38-981E-483FD3182E91,Illumina MiSeq,Listeria monocytogenes,Austria,22/12/2015,Austria,,,,,
diff --git a/sqljdbc42.jar b/sqljdbc42.jar
new file mode 100644
index 0000000..61b3463
Binary files /dev/null and b/sqljdbc42.jar differ
diff --git a/src/eu/europa/ecdc/enauploader/CsvOutputHandler.java b/src/eu/europa/ecdc/enauploader/CsvOutputHandler.java
new file mode 100644
index 0000000..39e7860
--- /dev/null
+++ b/src/eu/europa/ecdc/enauploader/CsvOutputHandler.java
@@ -0,0 +1,32 @@
+package eu.europa.ecdc.enauploader;
+
+import java.io.File;
+
+public class CsvOutputHandler extends OutputHandler {
+
+
+ String[][] data;
+ String[] header;
+ File outFile;
+ String project;
+
+ public CsvOutputHandler(File csvFile, File file, String proj) {
+ data = ENAUtils.readCsv(csvFile, true);
+ header = ENAUtils.readCsvHeader(csvFile);
+ outFile = file;
+ project = proj;
+ }
+
+ @Override
+ public void write(String value, int row, int col) {
+ data[row][col] = value;
+
+ }
+
+ @Override
+ public void close() {
+ ENAUtils.writeCsv(outFile,project,data,header);
+
+ }
+
+}
diff --git a/src/eu/europa/ecdc/enauploader/ENAUtils.java b/src/eu/europa/ecdc/enauploader/ENAUtils.java
index 27193a2..1dd6ab6 100644
--- a/src/eu/europa/ecdc/enauploader/ENAUtils.java
+++ b/src/eu/europa/ecdc/enauploader/ENAUtils.java
@@ -6,20 +6,11 @@
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
-import java.util.HashMap;
-
-import it.sauronsoftware.ftp4j.FTPAbortedException;
-import it.sauronsoftware.ftp4j.FTPClient;
-import it.sauronsoftware.ftp4j.FTPDataTransferException;
-import it.sauronsoftware.ftp4j.FTPException;
-import it.sauronsoftware.ftp4j.FTPIllegalReplyException;
+// Utils
public class ENAUtils {
-
-
-
-
+ // This method looks up and returns a taxid from a tab separated table
public static String getTaxid(String taxon) {
String out = "";
@@ -47,6 +38,7 @@ public static String getTaxid(String taxon) {
}
+ // This method reads the header of a csv file
public static String[] readCsvHeader(File csvFile) {
String line;
@@ -64,9 +56,9 @@ public static String[] readCsvHeader(File csvFile) {
return null;
}
+ // This method reads a csv file
public static String[][] readCsv(File csvFile, boolean header) {
-
String line;
ArrayList rowData = new ArrayList();
int cols = 0;
@@ -112,6 +104,7 @@ public static String[][] readCsv(File csvFile, boolean header) {
}
+ // This methods writes a csv file
public static void writeCsv(File file, String title, String[][] data, String[] header) {
BufferedWriter bw;
try {
diff --git a/src/eu/europa/ecdc/enauploader/ENAuploaderGUI.java b/src/eu/europa/ecdc/enauploader/ENAuploaderGUI.java
new file mode 100644
index 0000000..d574ba7
--- /dev/null
+++ b/src/eu/europa/ecdc/enauploader/ENAuploaderGUI.java
@@ -0,0 +1,1372 @@
+package eu.europa.ecdc.enauploader;
+
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.GridLayout;
+import java.awt.Toolkit;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.KeyEvent;
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.UUID;
+
+import javax.swing.BorderFactory;
+import javax.swing.BoxLayout;
+import javax.swing.JButton;
+import javax.swing.JCheckBox;
+import javax.swing.JComboBox;
+import javax.swing.JComponent;
+import javax.swing.JFileChooser;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JMenu;
+import javax.swing.JMenuBar;
+import javax.swing.JMenuItem;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JPasswordField;
+import javax.swing.JRadioButton;
+import javax.swing.JScrollPane;
+import javax.swing.JTable;
+import javax.swing.JTextArea;
+import javax.swing.JTextField;
+import javax.swing.KeyStroke;
+import javax.swing.ScrollPaneConstants;
+import javax.swing.UIManager;
+import javax.swing.UnsupportedLookAndFeelException;
+import javax.swing.event.CellEditorListener;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.TableModelEvent;
+import javax.swing.event.TableModelListener;
+import javax.swing.filechooser.FileSystemView;
+import javax.swing.table.DefaultTableCellRenderer;
+import javax.swing.table.DefaultTableModel;
+import javax.swing.text.DefaultCaret;
+
+
+public class ENAuploaderGUI extends JFrame implements ActionListener {
+
+ private static final int MAXROWS = 5000;
+ private static final String version="0.30";
+ private static final String BASE_TITLE = "ECDC uploader v"+version+" ";
+ private DefaultTableModel dataTableModel;
+ private JTable dataTable;
+ private JTextField projectText;
+ private JTextField centerText;
+ private JTextField projaccText;
+ private JTextField delimiterText;
+ private JTextField datadirText;
+ private JTextField loginText;
+ private JPasswordField passwdText;
+ private JCheckBox anonButton;
+ private JCheckBox prodButton;
+ private JTextArea logArea;
+
+ private String curlPath;
+ private String ftpHost;
+ private String tmpPath;
+ private JCheckBox ftpButton;
+ private JTextField holdText;
+ private JTextField checklistText;
+
+
+ private File saveFile;
+ private JTextField tessyLoginText;
+ private JPasswordField tessyPassText;
+ private JTextField tessyHostText;
+ private JTextField tessyDomainText;
+ private JTextField tessyTargetText;
+ private JTextField tessyContactText;
+ private JTextField tessyProviderText;
+ private JTextField tessySubjectText;
+ private JTextField tessyMetaText;
+ private JTextField tessyCountryText;
+ private JTextField ftpHostText;
+ private JTextField ftpLoginText;
+ private JPasswordField ftpPassText;
+ private JCheckBox ftpCheckBox;
+ private JCheckBox enaCheckBox;
+ private JCheckBox tessyCheckBox;
+ private JCheckBox tessyWarnCheckBox;
+ private JCheckBox tessyRemarkCheckBox;
+ private SimpleDateFormat timestamp;
+ ENAuploaderGUI gui;
+
+ ENAuploaderGUI() {
+ gui = this;
+ timestamp = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+ saveFile = null;
+ curlPath = "curl.exe";
+ String line;
+
+ try {
+ BufferedReader br = new BufferedReader(new FileReader(new File("paths.txt")));
+ while ((line = br.readLine())!=null) {
+ if (!line.equals("")) {
+ String[] fields = line.split("=");
+ String key = fields[0];
+ String value = fields[1];
+ if (key.equals("CURL")) {
+ curlPath = value;
+ } else if (key.equals("FTP")) {
+ ftpHost = value;
+ } else if (key.equals("TMP")) {
+ tmpPath = value;
+ }
+ }
+ }
+ br.close();
+ } catch (IOException e) {
+
+ e.printStackTrace();
+ }
+
+ JLabel checklistLabel = new JLabel("ENA checklist");
+ checklistText = new JTextField("ERC000044");
+ checklistText.setPreferredSize(new Dimension(60,20));
+
+ /*try {
+ BufferedReader br = new BufferedReader(new FileReader(new File("checklist.txt")));
+ while ((line = br.readLine())!=null) {
+ if (!line.equals("")) {
+ checklistText.setText(line.trim());
+ }
+ }
+ br.close();
+ } catch (IOException e) {
+
+ e.printStackTrace();
+ }*/
+
+
+
+ dataTable = new JTable(dataTableModel);
+ dataTable.setCellSelectionEnabled(true);
+ dataTable.getTableHeader().setReorderingAllowed(false);
+ new ExcelAdapter(dataTable);
+ //dataTable.putClientProperty("terminateEditOnFocusLost", true);
+
+
+
+
+ dataTable.setDefaultRenderer(Object.class, new DefaultTableCellRenderer() {
+ @Override
+ public Component getTableCellRendererComponent(
+ JTable table, Object value, boolean isSelected,
+ boolean hasFocus, int row, int column) {
+ Component c = super.getTableCellRendererComponent(
+ table, value, isSelected, hasFocus, row, column);
+ DefaultTableModel model = (DefaultTableModel) table.getModel();
+ c.setBackground(Color.white);
+ if (isSelected) {
+ c.setBackground(new Color(175, 175, 175));
+ } else if (table.getColumnName(column).endsWith("*")) {
+ if (table.getColumnName(column).startsWith("SFTP")) {
+ c.setBackground(new Color(189, 211, 125));
+ } else if (table.getColumnName(column).startsWith("TESSy")) {
+ c.setBackground(new Color(167, 182, 41));
+
+ if (table.getColumnName(column).equals("TESSy date last approved*")) {
+ try {
+ String val1 = (String)table.getValueAt(row, column);
+ String val2 = (String)table.getValueAt(row, column-1);
+ if (val1!=null && val2!=null && !val1.equals("") && !val2.equals("")) {
+ Date d1 = timestamp.parse(val1);
+ Date d2 = timestamp.parse(val2);
+ if (d2.after(d1)) {
+ c.setBackground(new Color(255, 180, 120));
+ }
+ }
+ } catch (ParseException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ } else if (table.getColumnName(column).equals("TESSy date last uploaded*")) {
+ try {
+ String val1 = (String)table.getValueAt(row, column);
+ String val2 = (String)table.getValueAt(row, 1);
+ if (val1!=null && val2!=null && !val1.equals("") && !val2.equals("")) {
+ Date d1 = timestamp.parse(val1);
+ Date d2 = timestamp.parse(val2);
+ if (d2.after(d1)) {
+ c.setBackground(new Color(255, 180, 120));
+ }
+ }
+ } catch (ParseException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+
+
+
+ } else {
+ c.setBackground(new Color(121, 191, 189));
+ }
+
+
+ } else {
+ if (table.getColumnName(column).matches(".*[;].+") || table.getColumnName(column).matches("Instrument.*") || table.getColumnName(column).matches("species.*") || table.getColumnName(column).matches("File base.*")) {
+ c.setBackground(new Color(235, 235, 255));
+ } else if (column<2) {
+
+ }
+ }
+
+
+
+ return c;
+ }
+ });
+
+
+
+ //loadMetaData(new File("metadata.txt"));
+
+ dataTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
+ JScrollPane dataScroller = new JScrollPane(dataTable);
+ dataScroller.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS );
+
+ JPanel bottomPanel = new JPanel(new BorderLayout());
+
+ JPanel dataPanel = new JPanel(new BorderLayout());
+ dataPanel.add(dataScroller,BorderLayout.CENTER);
+
+ JPanel ftpPanel = new JPanel();
+ GridLayout ftpLayout = new GridLayout(0,2);
+ ftpPanel.setLayout(ftpLayout);
+
+ JPanel controlPanel = new JPanel();
+ GridLayout buttonLayout = new GridLayout(0,2);
+ controlPanel.setLayout(buttonLayout);
+
+ JPanel tessyPanel = new JPanel();
+ GridLayout tessyLayout = new GridLayout(0,2);
+ tessyPanel.setLayout(tessyLayout);
+
+ JPanel buttonPanel = new JPanel();
+ buttonPanel.setLayout(new GridLayout(0,1));
+
+ ftpCheckBox = new JCheckBox("Submit to ECDC SFTP");
+ ftpCheckBox.setSelected(true);
+ JLabel fooLabel1 = new JLabel("");
+ ftpPanel.add(ftpCheckBox);
+ ftpPanel.add(fooLabel1);
+
+ enaCheckBox = new JCheckBox("Submit to ENA");
+ enaCheckBox.setSelected(true);
+ JLabel fooLabel2 = new JLabel("");
+ controlPanel.add(enaCheckBox);
+ controlPanel.add(fooLabel2);
+
+ tessyCheckBox = new JCheckBox("Submit to TESSy");
+ tessyCheckBox.setSelected(true);
+ JLabel fooLabel3 = new JLabel("");
+ tessyPanel.add(tessyCheckBox);
+ tessyPanel.add(fooLabel3);
+
+ JLabel ftpHostLabel = new JLabel("FTP host");
+ ftpHostText = new JTextField("sftp.ecdc.europa.eu");
+ ftpHostText.setPreferredSize(new Dimension(190,22));
+
+ JLabel ftpLoginLabel = new JLabel("FTP login");
+ ftpLoginText = new JTextField("Moltype");
+ ftpLoginText.setPreferredSize(new Dimension(190,22));
+
+ JLabel ftpPassLabel = new JLabel("FTP login");
+ ftpPassText = new JPasswordField("");
+ ftpPassText.setPreferredSize(new Dimension(190,22));
+
+ ftpPanel.add(ftpHostLabel);
+ ftpPanel.add(ftpHostText);
+
+ ftpPanel.add(ftpLoginLabel);
+ ftpPanel.add(ftpLoginText);
+
+ ftpPanel.add(ftpPassLabel);
+ ftpPanel.add(ftpPassText);
+
+ JLabel centerLabel = new JLabel("Submitting institution");
+ centerText = new JTextField("");
+ centerText.setPreferredSize(new Dimension(80,20));
+
+ controlPanel.add(centerLabel);
+ controlPanel.add(centerText);
+
+ JLabel projectLabel = new JLabel("Study alias");
+ projectText = new JTextField("");
+ projectText.setPreferredSize(new Dimension(60,20));
+
+ controlPanel.add(projectLabel);
+ controlPanel.add(projectText);
+
+ JLabel holdLabel = new JLabel("Release date (empty for immediate)");
+ SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
+ Calendar c = Calendar.getInstance();
+ c.setTime(new Date());
+ c.add(Calendar.DATE, 30);
+ String dateStr = format.format(c.getTime());
+ holdText = new JTextField(dateStr);
+ holdText.setPreferredSize(new Dimension(60,20));
+
+ controlPanel.add(holdLabel);
+ controlPanel.add(holdText);
+
+ JLabel projaccLabel = new JLabel("Study accession");
+ projaccText = new JTextField("");
+ projaccText.setPreferredSize(new Dimension(60,20));
+
+ controlPanel.add(projaccLabel);
+ controlPanel.add(projaccText);
+
+ JLabel createProjectLabel = new JLabel("");
+ JButton createProjectButton = new JButton("Create and submit study to ENA");
+ createProjectButton.setActionCommand("newProject");
+ createProjectButton.addActionListener(this);
+
+
+
+ JLabel releaseProjectLabel = new JLabel("");
+ JButton releaseProjectButton = new JButton("Release ENA study");
+ releaseProjectButton.setActionCommand("releaseProject");
+ releaseProjectButton.addActionListener(this);
+
+
+
+
+ JLabel delimiterLabel = new JLabel("File delimiter");
+ delimiterText = new JTextField("_");
+ delimiterText.setPreferredSize(new Dimension(60,20));
+
+ controlPanel.add(delimiterLabel);
+ controlPanel.add(delimiterText);
+
+ JLabel datadirLabel = new JLabel("Data directory");
+ datadirText = new JTextField("");
+ datadirText.setPreferredSize(new Dimension(60,20));
+
+ controlPanel.add(datadirLabel);
+ controlPanel.add(datadirText);
+
+ JLabel choosedirLabel = new JLabel("");
+ JButton choosedirButton = new JButton("Choose data directory");
+ choosedirButton.setActionCommand("chooseDir");
+ choosedirButton.addActionListener(this);
+
+ controlPanel.add(choosedirLabel);
+ controlPanel.add(choosedirButton);
+
+ JLabel submitLabel = new JLabel("");
+ JButton submitButton = new JButton("Submit isolates to selected systems");
+ submitButton.setActionCommand("submitIsolates");
+ submitButton.addActionListener(this);
+
+
+
+
+ buttonPanel.add(submitButton);
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ JLabel loginLabel = new JLabel("Webin account");
+ loginText = new JTextField("");
+ loginText.setPreferredSize(new Dimension(60,20));
+
+ controlPanel.add(loginLabel);
+ controlPanel.add(loginText);
+
+ JLabel passwdLabel = new JLabel("Webin password");
+ passwdText = new JPasswordField("");
+ passwdText.setPreferredSize(new Dimension(60,20));
+
+ controlPanel.add(passwdLabel);
+ controlPanel.add(passwdText);
+
+
+ JLabel anonLabel = new JLabel("");
+ anonButton = new JCheckBox ("Anonymize sample IDs");
+ anonButton.setSelected(true);
+
+ controlPanel.add(anonLabel);
+ controlPanel.add(anonButton);
+
+ JLabel prodLabel = new JLabel("");
+ prodButton = new JCheckBox ("Use ENA production API");
+
+ controlPanel.add(prodLabel);
+ controlPanel.add(prodButton);
+
+ JLabel ftpLabel = new JLabel("");
+ ftpButton = new JCheckBox ("Files already exist on ENA FTP");
+
+ controlPanel.add(ftpLabel);
+ controlPanel.add(ftpButton);
+
+
+ controlPanel.add(checklistLabel);
+ controlPanel.add(checklistText);
+
+
+ controlPanel.add(createProjectLabel);
+ controlPanel.add(createProjectButton);
+ controlPanel.add(releaseProjectLabel);
+ controlPanel.add(releaseProjectButton);
+
+ JLabel tessyHostLabel = new JLabel("TESSy URL");
+ tessyHostText = new JTextField("tessy.ecdc.europa.eu");
+ tessyHostText.setPreferredSize(new Dimension(120,20));
+ tessyPanel.add(tessyHostLabel);
+ tessyPanel.add(tessyHostText);
+
+ JLabel tessyDomainLabel = new JLabel("TESSy DOMAIN");
+ tessyDomainText = new JTextField("ecdcdmz");
+ tessyDomainText.setPreferredSize(new Dimension(120,20));
+ tessyPanel.add(tessyDomainLabel);
+ tessyPanel.add(tessyDomainText);
+
+ JLabel tessyTargetLabel = new JLabel("TESSy TARGET");
+ tessyTargetText = new JTextField("/TessyWebService/TessyUpload.asmx");
+ tessyTargetText.setPreferredSize(new Dimension(120,20));
+ tessyPanel.add(tessyTargetLabel);
+ tessyPanel.add(tessyTargetText);
+
+
+ JLabel tessyLoginLabel = new JLabel("TESSy account");
+ tessyLoginText = new JTextField("");
+ tessyLoginText.setPreferredSize(new Dimension(120,20));
+ tessyPanel.add(tessyLoginLabel);
+ tessyPanel.add(tessyLoginText);
+
+ JLabel tessyPassLabel = new JLabel("TESSy password");
+ tessyPassText = new JPasswordField("");
+ tessyPassText.setPreferredSize(new Dimension(120,20));
+ tessyPanel.add(tessyPassLabel);
+ tessyPanel.add(tessyPassText);
+
+ JLabel tessyCountryLabel = new JLabel("TESSy country");
+ tessyCountryText = new JTextField("");
+ tessyCountryText.setPreferredSize(new Dimension(120,20));
+ tessyPanel.add(tessyCountryLabel);
+ tessyPanel.add(tessyCountryText);
+
+ JLabel tessyContactLabel = new JLabel("TESSy contact");
+ tessyContactText = new JTextField("");
+ tessyContactText.setPreferredSize(new Dimension(120,20));
+ tessyPanel.add(tessyContactLabel);
+ tessyPanel.add(tessyContactText);
+
+ JLabel tessyProviderLabel = new JLabel("TESSy data source (e.g. XX-MOLSURV)");
+ tessyProviderText = new JTextField("");
+ tessyProviderText.setPreferredSize(new Dimension(120,20));
+ tessyPanel.add(tessyProviderLabel);
+ tessyPanel.add(tessyProviderText);
+
+ JLabel tessySubjectLabel = new JLabel("TESSy SUBJECT (e.g. SALMISO)");
+ tessySubjectText = new JTextField("SALMISO");
+ tessySubjectText.setPreferredSize(new Dimension(120,20));
+ tessyPanel.add(tessySubjectLabel);
+ tessyPanel.add(tessySubjectText);
+
+ JLabel tessyMetaLabel = new JLabel("TESSy metadata version");
+ tessyMetaText = new JTextField("3");
+ tessyMetaText.setPreferredSize(new Dimension(120,20));
+ tessyPanel.add(tessyMetaLabel);
+ tessyPanel.add(tessyMetaText);
+
+
+ JLabel tessyWarnLabel = new JLabel("");
+ tessyWarnCheckBox = new JCheckBox("Halt submission on warning");
+ tessyPanel.add(tessyWarnLabel);
+ tessyPanel.add(tessyWarnCheckBox);
+
+ JLabel tessyRemarkLabel = new JLabel("");
+ tessyRemarkCheckBox = new JCheckBox("Halt submission on remark");
+ tessyPanel.add(tessyRemarkLabel);
+ tessyPanel.add(tessyRemarkCheckBox);
+
+
+ logArea = new JTextArea("");
+ DefaultCaret caret = (DefaultCaret)logArea.getCaret();
+ caret.setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE);
+
+ JScrollPane logScroller = new JScrollPane(logArea);
+ logScroller.setPreferredSize(new Dimension(1000,200));
+ bottomPanel.add(logScroller);
+
+ JPanel rightPanel = new JPanel();
+ rightPanel.setPreferredSize(new Dimension(440,1200));
+ rightPanel.add(ftpPanel);
+ rightPanel.add(controlPanel);
+ rightPanel.add(tessyPanel);
+ rightPanel.add(buttonPanel);
+
+ JScrollPane rightScroller = new JScrollPane(rightPanel);
+
+
+ JPanel mainPanel = new JPanel(new BorderLayout());
+
+ ftpPanel.setBorder(BorderFactory.createTitledBorder("ECDC FTP parameters"));
+ controlPanel.setBorder(BorderFactory.createTitledBorder("ENA parameters"));
+ tessyPanel.setBorder(BorderFactory.createTitledBorder("TESSy parameters"));
+ buttonPanel.setBorder(BorderFactory.createTitledBorder("Controls"));
+ dataPanel.setBorder(BorderFactory.createTitledBorder("Isolate table"));
+
+
+ createMenuBar();
+
+
+ mainPanel.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));
+
+
+
+ mainPanel.add(dataPanel,BorderLayout.CENTER);
+ mainPanel.add(rightScroller,BorderLayout.EAST);
+
+
+ mainPanel.add(bottomPanel,BorderLayout.SOUTH);
+
+ setTitle(BASE_TITLE);
+ this.getContentPane().add(mainPanel);
+ this.setDefaultCloseOperation(EXIT_ON_CLOSE);
+ this.setSize(new Dimension(1200,800));
+
+ File stateFile = new File("lastopen.txt");
+ if (stateFile.exists()) {
+ try {
+ BufferedReader br = new BufferedReader(new FileReader(stateFile));
+
+ loadState(new File(br.readLine()));
+ br.close();
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ }
+
+
+
+ setVisible(true);
+
+ }
+
+
+ private void createMenuBar() {
+ JMenuBar menuBar = new JMenuBar();
+ JMenu fileMenu = new JMenu("File");
+
+ JMenuItem newMetaMenuItem = new JMenuItem("New metadataset");
+ newMetaMenuItem.addActionListener(this);
+ newMetaMenuItem.setActionCommand("NewMeta");
+ fileMenu.add(newMetaMenuItem);
+
+ fileMenu.addSeparator();
+
+ JMenuItem newMenuItem = new JMenuItem("New dataset");
+ newMenuItem.addActionListener(this);
+ newMenuItem.setActionCommand("New");
+ fileMenu.add(newMenuItem);
+ KeyStroke ctrlN = KeyStroke.getKeyStroke(KeyEvent.VK_N, Toolkit.getDefaultToolkit ().getMenuShortcutKeyMask());
+ newMenuItem.setAccelerator(ctrlN);
+
+
+
+ JMenuItem openMenuItem = new JMenuItem("Open");
+ openMenuItem.addActionListener(this);
+ openMenuItem.setActionCommand("Open");
+ fileMenu.add(openMenuItem);
+ KeyStroke ctrlO = KeyStroke.getKeyStroke(KeyEvent.VK_O, Toolkit.getDefaultToolkit ().getMenuShortcutKeyMask());
+ openMenuItem.setAccelerator(ctrlO);
+
+ JMenuItem saveMenuItem = new JMenuItem("Save");
+ saveMenuItem.addActionListener(this);
+ saveMenuItem.setActionCommand("Save");
+ fileMenu.add(saveMenuItem);
+ KeyStroke ctrlS = KeyStroke.getKeyStroke(KeyEvent.VK_S, Toolkit.getDefaultToolkit ().getMenuShortcutKeyMask());
+ saveMenuItem.setAccelerator(ctrlS);
+
+ JMenuItem saveAsMenuItem = new JMenuItem("Save as...");
+ saveAsMenuItem.addActionListener(this);
+ saveAsMenuItem.setActionCommand("SaveAs");
+ fileMenu.add(saveAsMenuItem);
+
+ fileMenu.addSeparator();
+
+
+ JMenuItem quickSaveMenuItem = new JMenuItem("Quick save");
+ quickSaveMenuItem.addActionListener(this);
+ quickSaveMenuItem.setActionCommand("qSave");
+ fileMenu.add(quickSaveMenuItem);
+
+ JMenuItem quickLoadMenuItem = new JMenuItem("Quick load");
+ quickLoadMenuItem.addActionListener(this);
+ quickLoadMenuItem.setActionCommand("qLoad");
+ fileMenu.add(quickLoadMenuItem);
+
+ fileMenu.addSeparator();
+ JMenuItem instrumentsMenuItem = new JMenuItem("Show allowed instruments");
+ instrumentsMenuItem.addActionListener(this);
+ instrumentsMenuItem.setActionCommand("Instruments");
+ fileMenu.add(instrumentsMenuItem);
+
+ JMenuItem taxMenuItem = new JMenuItem("Show allowed species");
+ taxMenuItem.addActionListener(this);
+ taxMenuItem.setActionCommand("Taxids");
+ fileMenu.add(taxMenuItem);
+
+ menuBar.add(fileMenu);
+ this.setJMenuBar(menuBar);
+
+ }
+
+
+ public static void main(String[] args) {
+ try {
+ // Set System L&F
+ UIManager.setLookAndFeel(
+ UIManager.getSystemLookAndFeelClassName());
+ }
+ catch (UnsupportedLookAndFeelException e) {
+ // handle exception
+ }
+ catch (ClassNotFoundException e) {
+ // handle exception
+ }
+ catch (InstantiationException e) {
+ // handle exception
+ }
+ catch (IllegalAccessException e) {
+ // handle exception
+ }
+
+ new ENAuploaderGUI();
+ }
+
+
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ if (e.getActionCommand().equals("newProject")) {
+ submitProject();
+ } else if (e.getActionCommand().equals("releaseProject")) {
+ releaseProject();
+ } else if (e.getActionCommand().equals("submitIsolates")) {
+ submitIsolates(ftpCheckBox.isSelected(),enaCheckBox.isSelected(),tessyCheckBox.isSelected());
+ } else if (e.getActionCommand().equals("chooseDir")) {
+ chooseDir();
+ } else if (e.getActionCommand().equals("qSave")) {
+ saveState(new File("qsave.txt"));
+ } else if (e.getActionCommand().equals("qLoad")) {
+ loadState(new File("qsave.txt"));
+ } else if (e.getActionCommand().equals("Instruments")) {
+ JFrame frame = new JFrame("Allowed instruments");
+ JPanel panel = new JPanel(new BorderLayout());
+ JTextArea area = new JTextArea("");
+ area.setEditable(false);
+ panel.add(area,BorderLayout.CENTER);
+ frame.getContentPane().add(panel);
+ frame.setSize(400,600);
+ frame.setVisible(true);
+ area.setText("HiSeq X Five\nHiSeq X Ten\nIllumina Genome Analyzer\nIllumina Genome Analyzer II\nIllumina Genome Analyzer IIx\nIllumina HiScanSQ\nIllumina HiSeq 1000\nIllumina HiSeq 1500\nIllumina HiSeq 2000\nIllumina HiSeq 2500\nIllumina HiSeq 3000\nIllumina HiSeq 4000\nIllumina MiSeq\nIllumina MiniSeq\nIllumina NovaSeq 6000\nNextSeq 500\nNextSeq 550\n");
+ area.append("\nIon Torrent PGM\nIon Torrent Proton\nIon Torrent S5\nIon Torrent S5 XL\n");
+ area.append("\nPacBio RS\nPacBio RS II\nSequel");
+
+
+ }else if (e.getActionCommand().equals("Taxids")) {
+ JFrame frame = new JFrame("Allowed species");
+ JPanel panel = new JPanel(new BorderLayout());
+ JTextArea area = new JTextArea("");
+ area.setEditable(false);
+ JScrollPane scroll = new JScrollPane(area);
+ panel.add(scroll,BorderLayout.CENTER);
+ frame.getContentPane().add(panel);
+ frame.setSize(400,600);
+ frame.setVisible(true);
+ try {
+ String line;
+ BufferedReader br = new BufferedReader(new FileReader("taxids.tsv"));
+
+ while((line = br.readLine())!=null) {
+ area.append(line+"\n");
+ }
+
+ br.close();
+
+ } catch (IOException e2) {
+ e2.printStackTrace();
+ }
+
+
+ } else if (e.getActionCommand().equals("Save")) {
+ save();
+ } else if (e.getActionCommand().equals("SaveAs")) {
+ saveAs();
+
+ } else if (e.getActionCommand().equals("Open")) {
+ JFileChooser jfc = new JFileChooser(FileSystemView.getFileSystemView().getHomeDirectory());
+
+ int returnValue = jfc.showOpenDialog(null);
+ //int returnValue = jfc.showSaveDialog(null);
+
+ if (returnValue == JFileChooser.APPROVE_OPTION) {
+ File selectedFile = jfc.getSelectedFile();
+ loadState(selectedFile);
+ saveFile = selectedFile;
+
+ }
+ } else if (e.getActionCommand().equals("New")) {
+ JComboBox metaBox = new JComboBox();
+ File[] files = (new File(".")).listFiles();
+ for (File f : files) {
+ if (f.getName().startsWith("ERC")||f.getName().startsWith("meta")||f.getName().endsWith("ISO.txt")) {
+ metaBox.addItem(f.getName());
+ }
+ }
+ JTextActionArea textArea = new JTextActionArea("",false);
+ textArea.setPreferredSize(new Dimension(140,400));
+ metaBox.addActionListener(textArea);
+ textArea.actionPerformed(new ActionEvent(metaBox, 0, ""));
+
+ final JComponent[] inputs = new JComponent[] {
+ new JLabel("Select metadataset"),
+ metaBox,textArea
+
+ };
+ int result = JOptionPane.showConfirmDialog(null, inputs, "Choose metadata set", JOptionPane.PLAIN_MESSAGE);
+ if (result == JOptionPane.OK_OPTION) {
+ String val = metaBox.getSelectedItem().toString();
+ loadMetaData(new File(val));
+ if (val.length()>=13) {
+ checklistText.setText(val.substring(0,9));
+ }
+
+ setTitle(BASE_TITLE + " - "+ "untitled");
+ } else {
+
+ }
+
+ } else if (e.getActionCommand().equals("NewMeta")) {
+ JTextField metaField = new JTextField("");
+ JComboBox metaBox = new JComboBox();
+ File[] files = (new File(".")).listFiles();
+ for (File f : files) {
+ if (f.getName().startsWith("ERC")||f.getName().startsWith("meta")) {
+ metaBox.addItem(f.getName());
+ }
+ }
+ JTextActionArea textArea = new JTextActionArea("",true);
+ textArea.setPreferredSize(new Dimension(140,400));
+ metaBox.addActionListener(textArea);
+ textArea.actionPerformed(new ActionEvent(metaBox, 0, ""));
+
+ final JComponent[] inputs = new JComponent[] {
+ new JLabel("Name of new metadataset"),
+ metaField,
+ new JLabel("Existing metadatasets"),
+ metaBox,
+ new JLabel("Metadata fields in new set"),
+ textArea
+
+ };
+ int result = JOptionPane.showConfirmDialog(null, inputs, "Create metadata set", JOptionPane.PLAIN_MESSAGE);
+ if (result == JOptionPane.OK_OPTION) {
+ String val = metaField.getText();
+ String metastr = textArea.getText();
+ String[] fields = metastr.split("\n");
+ try {
+ BufferedWriter bw = new BufferedWriter(new FileWriter(new File(val)));
+ for (String l:fields) {
+ bw.write(l+"\n");
+ }
+ bw.close();
+ } catch (IOException e2) {
+ e2.printStackTrace();
+ }
+ } else {
+
+ }
+
+ }
+
+ }
+
+
+ public void save() {
+ if (saveFile==null) {
+ saveAs();
+ return;
+ }
+ saveState(saveFile);
+
+ }
+
+ private void saveAs() {
+ JFileChooser jfc = new JFileChooser(FileSystemView.getFileSystemView().getHomeDirectory());
+
+ int returnValue = jfc.showSaveDialog(null);
+
+ if (returnValue == JFileChooser.APPROVE_OPTION) {
+ File selectedFile = jfc.getSelectedFile();
+ saveState(selectedFile);
+ saveFile = selectedFile;
+ setTitle(BASE_TITLE + " - "+ saveFile.toString());
+ }
+
+ }
+
+
+ private void chooseDir() {
+ JFileChooser chooser = new JFileChooser();
+ chooser.setCurrentDirectory(new java.io.File("."));
+ chooser.setDialogTitle("Choose directory for fastq files");
+ chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
+ chooser.setAcceptAllFileFilterUsed(false);
+ if (chooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
+ datadirText.setText(chooser.getSelectedFile().toString());
+ }
+ else {
+ System.out.println("No Selection ");
+ }
+
+ }
+
+
+ private void submitIsolates(boolean ftp, boolean ena, boolean tessy) {
+
+ String sftpHost= ftpHostText.getText();
+ String sftpLogin = ftpLoginText.getText();
+ char[] ftpPasswdchars = ftpPassText.getPassword();
+ String sftpPass = new String(ftpPasswdchars);
+
+ String tessyLogin = tessyLoginText.getText();
+ String tessyUrl = tessyHostText.getText();
+ String tessyDomain = tessyDomainText.getText();
+ String tessyTarget = tessyTargetText.getText();
+ String tessyContact = tessyContactText.getText();
+ String tessyProvider = tessyProviderText.getText();
+ String tessySubject = tessySubjectText.getText();
+ String tessyMeta = tessyMetaText.getText();
+ String tessyCountry = tessyCountryText.getText();
+
+ char[] tessyPasswdchars = tessyPassText.getPassword();
+ String tessyPass = new String(tessyPasswdchars);
+
+
+ TessyCredentials cred = null;
+ TessyBatch batch = null;
+ if (tessy) {
+ if (tessyPass.equals("") || tessyLogin.equals("") || tessyUrl.equals("") || tessyDomain.equals("") || tessyTarget.equals("")) {
+ JOptionPane.showMessageDialog(this, "You must enter TESSy login information to submit to TESSy");
+ return;
+ } else {
+
+ cred = new TessyCredentials();
+ cred.setUsername(tessyLogin);
+ cred.setPassword(tessyPasswdchars);
+ cred.setDomain(tessyDomain);
+ cred.setHostname(tessyUrl);
+ cred.setTarget(tessyTarget);
+ }
+ boolean haltRemark = tessyRemarkCheckBox.isSelected();
+ boolean haltWarn = tessyWarnCheckBox.isSelected();
+
+
+
+
+ batch = new TessyBatch("",tessyCountry,tessyContact, tessyProvider, tessyMeta, tessySubject);
+ batch.setTessyCredentials(cred);
+ batch.setHaltWarn(haltWarn);
+ batch.setHaltRemark(haltRemark);
+
+ }
+
+ String projectId = projaccText.getText();
+ String center = centerText.getText();
+ File dataDir = new File(datadirText.getText());
+
+ boolean anonymize = anonButton.isSelected();
+ boolean prod = prodButton.isSelected();
+ boolean ftpExist = ftpButton.isSelected();
+
+
+
+ String delimiter = delimiterText.getText();
+ String login = loginText.getText();
+
+ String enaChecklist = checklistText.getText();
+
+ char[] passwdchars = passwdText.getPassword();
+ String pass = new String(passwdchars);
+
+ if (ena) {
+ if (center.equals("")) {
+ JOptionPane.showMessageDialog(this, "You must enter an institution");
+ return;
+ }
+ if (projectId.equals("")) {
+ JOptionPane.showMessageDialog(this, "You must enter an existing study acc or create a study acc by submitting a study first.");
+ return;
+ }
+ if (!dataDir.exists()) {
+ if (!ftpExist) {
+ JOptionPane.showMessageDialog(this, "You must choose a valid data directory unless files are already on FTP.");
+ return;
+ } else {
+
+ }
+ }
+ }
+
+ if (ftp) {
+ if (sftpHost.equals("")) {
+ JOptionPane.showMessageDialog(this, "You must enter an SFTP host for SFTP upload.");
+ return;
+ }
+ if (sftpLogin.equals("")) {
+ JOptionPane.showMessageDialog(this, "You must enter an SFTP login for SFTP upload.");
+ return;
+ }
+ if (sftpPass.equals("")) {
+ JOptionPane.showMessageDialog(this, "You must enter an SFTP password for SFTP upload.");
+ return;
+ }
+ }
+
+ int rows = 0;
+ for (int i = 0;i dataTable.getColumnCount()-7) {
+ return;
+ }
+
+ int row1 = e.getFirstRow();
+ int row2 = e.getLastRow();
+ for (int i = row1;i<=row2;i++) {
+ if (dataTableModel.getValueAt(i,0)!=null) {
+ dataTableModel.setValueAt(timestamp.format(new Date()), i, 1);
+ }
+ }
+
+ if (saveFile!=null) {
+ save();
+ }
+ }
+
+
+ });
+
+
+ }
+
+
+ public void saveState(File f) {
+
+
+
+
+ String ftpHost = ftpHostText.getText();
+ String ftpLogin = ftpLoginText.getText();
+
+ boolean useFtp = ftpCheckBox.isSelected();
+ boolean useEna = enaCheckBox.isSelected();
+ boolean useTessy = tessyCheckBox.isSelected();
+
+ boolean haltWarn = tessyWarnCheckBox.isSelected();
+ boolean haltRemark = tessyRemarkCheckBox.isSelected();
+
+ String tessyLogin = tessyLoginText.getText();
+ String tessyUrl = tessyHostText.getText();
+ String tessyDomain = tessyDomainText.getText();
+ String tessyTarget = tessyTargetText.getText();
+ String tessyContact = tessyContactText.getText();
+ String tessyProvider = tessyProviderText.getText();
+ String tessySubject = tessySubjectText.getText();
+ String tessyMeta = tessyMetaText.getText();
+
+ String tessyCountry = tessyCountryText.getText();
+
+
+ String projectId = projaccText.getText();
+ String center = centerText.getText();
+ String alias = projectText.getText();
+ String dataDir = datadirText.getText();
+
+ boolean anonymize = anonButton.isSelected();
+ boolean prod = prodButton.isSelected();
+ boolean ftpExist = ftpButton.isSelected();
+
+
+
+ String enaChecklist = checklistText.getText();
+
+ String delimiter = delimiterText.getText();
+ String login = loginText.getText();
+ try {
+ BufferedWriter bw = new BufferedWriter(new FileWriter(f));
+ bw.write("PROJECT_ALIAS="+alias+"\n");
+ bw.write("PROJECT_ACC="+projectId+"\n");
+ bw.write("CENTRE="+center+"\n");
+ bw.write("DATA_DIR="+dataDir+"\n");
+ bw.write("ANONYMIZE="+Boolean.toString(anonymize)+"\n");
+ bw.write("PRODUCTION="+Boolean.toString(prod)+"\n");
+ bw.write("FTP_EXISTS="+Boolean.toString(ftpExist)+"\n");
+ bw.write("DELIMITER="+delimiter+"\n");
+ bw.write("LOGIN="+login+"\n");
+ bw.write("CHECKLIST="+enaChecklist+"\n");
+ bw.write("TESSYLOGIN="+tessyLogin+"\n");
+ bw.write("TESSYURL="+tessyUrl+"\n");
+ bw.write("TESSYDOMAIN="+tessyDomain+"\n");
+ bw.write("TESSYTARGET="+tessyTarget+"\n");
+ bw.write("TESSYCONTACT="+tessyContact+"\n");
+ bw.write("TESSYPROVIDER="+tessyProvider+"\n");
+ bw.write("TESSYSUBJECT="+tessySubject+"\n");
+ bw.write("TESSYMETA="+tessyMeta+"\n");
+ bw.write("TESSYCOUNTRY="+tessyCountry+"\n");
+ bw.write("FTPHOST="+ftpHost+"\n");
+ bw.write("FTPLOGIN="+ftpLogin+"\n");
+ bw.write("USEFTP="+Boolean.toString(useFtp)+"\n");
+ bw.write("USEENA="+Boolean.toString(useEna)+"\n");
+ bw.write("USETESSY="+Boolean.toString(useTessy)+"\n");
+ bw.write("HALTWARN="+Boolean.toString(haltWarn)+"\n");
+ bw.write("HALTREMARK="+Boolean.toString(haltRemark)+"\n");
+
+ String headRow = "";
+ for (int j = 0;j headFields = new ArrayList();
+ headFields.add("ID");
+ headFields.add("Last modified");
+
+ String line;
+ try {
+ BufferedReader br = new BufferedReader(new FileReader(infile));
+ while ((line = br.readLine())!=null) {
+ if (!line.equals("")) {
+ headFields.add(line.trim());
+ }
+ }
+ br.close();
+ } catch (IOException e) {
+
+ e.printStackTrace();
+ }
+
+ headFields.add("Instrument model");
+ headFields.add("species");
+ headFields.add("File base name");
+ headFields.add("ENA Anonymized id*");
+ headFields.add("ENA Sample acc*");
+ headFields.add("ENA Experiment acc*");
+ headFields.add("ENA Run acc*");
+ headFields.add("ENA Uploaded files*");
+ headFields.add("TESSy batch*");
+ headFields.add("TESSy validation*");
+ headFields.add("TESSy date last uploaded*");
+ headFields.add("TESSy date last approved*");
+ headFields.add("TESSy id*");
+ headFields.add("SFTP uploaded date*");
+
+
+
+ String [] header = (String[]) headFields.toArray(new String[0]);
+ String[][] data = new String[MAXROWS][header.length];
+ dataTableModel = new DefaultTableModel(data,header);
+ dataTable.setModel(dataTableModel);
+ addTableListener();
+
+ }
+
+
+
+}
diff --git a/src/eu/europa/ecdc/enauploader/ENAuploaderMain.java b/src/eu/europa/ecdc/enauploader/ENAuploaderMain.java
index 58a956d..3520687 100644
--- a/src/eu/europa/ecdc/enauploader/ENAuploaderMain.java
+++ b/src/eu/europa/ecdc/enauploader/ENAuploaderMain.java
@@ -20,7 +20,7 @@
public class ENAuploaderMain {
- private static String version="0.20";
+ private static String version="0.22";
// This is a command line version
@@ -33,7 +33,7 @@ public static void main(String[] args) {
// Login (Webin-NNNNN)
// Password
// yes/no whether to use the production database at EBI
- // yes/no whether to anonymize
+ // yes/no whether to anonymizehttp://marketplace.eclipse.org/marketplace-client-intro?mpc_install=156
// yes/no whether files are already on FTP
//delimiter
//ENA checklist
@@ -144,10 +144,24 @@ public static void main(String[] args) {
passwd = new String(System.console().readPassword());
}
+ boolean prod=false;
+ if (commandLine.hasOption("production")) {
+ String anonStr = commandLine.getOptionValue("production");
+ if (anonStr.equals("yes")) {
+ prod = true;
+ } else if (anonStr.equals("no")) {
+ prod = false;
+ } else {
+ System.out.println("--production must be yes or no.");
+ return;
+ }
+ }
+
+
String release;
if (commandLine.hasOption("r")) {
release = commandLine.getOptionValue("r");
- releaseStudy(center,release,curlPath,tmpPath,login,passwd);
+ releaseStudy(center,release,curlPath,tmpPath,login,passwd,prod);
return;
} else {
@@ -221,18 +235,7 @@ public static void main(String[] args) {
return;
}
}
- boolean prod=false;
- if (commandLine.hasOption("production")) {
- String anonStr = commandLine.getOptionValue("production");
- if (anonStr.equals("yes")) {
- prod = true;
- } else if (anonStr.equals("no")) {
- prod = false;
- } else {
- System.out.println("--production must be yes or no.");
- return;
- }
- }
+
boolean ftpExist=false;
if (commandLine.hasOption("ftp")) {
String anonStr = commandLine.getOptionValue("ftp");
@@ -270,6 +273,11 @@ public static void main(String[] args) {
s.setCurlPath(curlPath);
s.setTmpPath(tmpPath);
+ if (prod) {
+ s.useProductionServer(true);
+ } else {
+ s.useProductionServer(false);
+ }
Project p = new Project(center,project,s);
p.setReleaseDate(holdDate);
@@ -302,7 +310,7 @@ public static void main(String[] args) {
String[] header = ENAUtils.readCsvHeader(csvFile);
CsvOutputHandler outHandler = new CsvOutputHandler(csvFile,outCsv,project);
- SubmissionWorker worker = new SubmissionWorker(center, project, data, header, dataDir,login,passwd, prod, anon, ftpExist, delimiter, checklist, null, tmpPath, curlPath, ftpHost, outHandler);
+ SubmissionWorker worker = new SubmissionWorker(center, project, data, header, dataDir,login,passwd, prod, anon, ftpExist, delimiter, checklist, null, tmpPath, curlPath, ftpHost, outHandler, null,"","","", false, true, false, null);
worker.doInBackground();
}
@@ -331,13 +339,16 @@ public static void main(String[] args) {
}
- private static void releaseStudy(String center, String acc, String curlPath, String tmpPath, String login, String passwd) {
+ private static void releaseStudy(String center, String acc, String curlPath, String tmpPath, String login, String passwd, boolean prod) {
String randomUUIDString = UUID.randomUUID().toString();
Submission s = new Submission(center, randomUUIDString);
s.setCurlPath(curlPath);
s.setTmpPath(tmpPath);
-
+ if (prod) {
+ s.useProductionServer(true);
+ }
+
Project p = new Project(center,"",s);
p.setAccession(acc);
p.setReleaseAction(true);
diff --git a/src/eu/europa/ecdc/enauploader/EcdcAutomationWorker.java b/src/eu/europa/ecdc/enauploader/EcdcAutomationWorker.java
new file mode 100644
index 0000000..9cd2c99
--- /dev/null
+++ b/src/eu/europa/ecdc/enauploader/EcdcAutomationWorker.java
@@ -0,0 +1,179 @@
+package eu.europa.ecdc.enauploader;
+
+import java.util.ArrayList;
+
+import javax.swing.JTable;
+
+// This class automates the process of Data import, sequence file linking and data submission
+public class EcdcAutomationWorker extends EcdcJob {
+
+ private ImportConfig importConfig;
+ private JTable table;
+
+ EcdcAutomationWorker(EcdcUploaderGUI gui, TessyBatch batch, String name, UploadConfig cfg, String[] headers,
+ String[][] data) {
+ super(gui, batch, name, cfg, headers, data);
+
+ }
+
+ public void setImportConfig (ImportConfig cfg) {
+ this.importConfig = cfg;
+ }
+
+ public void setTable (JTable table) {
+ this.table = table;
+ }
+
+ @Override
+ protected Object doInBackground() throws Exception {
+
+ setTitle("Automated import, linking and submission");
+ setStatus("Initializing");
+ setProgress(10);
+
+ // Create EcdcImportWorker for data import
+ EcdcImportWorker importWorker = gui.getEcdcImportWorker(name);
+
+
+ // Check if this job was interrupted
+ if (isStopped()) {
+ log("Job interrupted, exiting");
+ setStatus("Error, job interrupted");
+ setProgress(30);
+ return null;
+ }
+
+
+ // If the worker was successfully created, run it and wait for the job to finish
+ if (importWorker!=null) {
+ setStatus("Importing...");
+ setProgress(25);
+ importWorker.execute();
+ boolean done=false;
+ while (!done) {
+ try {
+ Thread.sleep(5000);
+ } catch (InterruptedException e) {
+ }
+
+ // Check if this job was interrupted
+ if (importWorker.isStopped()) {
+ log("Job interrupted, exiting");
+ setStatus("Error, job interrupted");
+ setProgress(30);
+ return null;
+ }
+ if (importWorker.isDone()) {
+ done=true;
+ }
+ }
+ } else {
+ log("GUI returned null ImportWorker, exiting");
+ setStatus("Error, null ImportWorker");
+ setProgress(35);
+ return null;
+ }
+
+ setStatus("Linking sequence data...");
+ setProgress(35);
+ int[] selected = new int[table.getRowCount()];
+ for (int i = 0; i< selected.length;i++) {
+ selected[i] = i;
+ }
+ EcdcLinkWorker linkWorker = gui.getEcdcLinkWorker(importConfig.getSubject(), selected);
+
+ if (isStopped()) {
+ log("Job interrupted, exiting");
+ setStatus("Error, job interrupted");
+ setProgress(40);
+ return null;
+ }
+
+
+
+ // If the worker was successfully created, run it and wait for the job to finish
+ if (linkWorker!=null) {
+ setProgress(37);
+ linkWorker.execute();
+ boolean done=false;
+ while (!done) {
+ try {
+ Thread.sleep(5000);
+ } catch (InterruptedException e) {
+ }
+
+ // Check if this job was interrupted
+ if (linkWorker.isStopped()) {
+ log("Job interrupted, exiting");
+ setStatus("Error, job interrupted");
+ setProgress(45);
+ return null;
+ }
+ if (linkWorker.isDone()) {
+ done=true;
+
+ }
+ }
+ } else {
+ log("GUI returned null LinkWorker, exiting");
+ setStatus("Error, null LinkWorker");
+ setProgress(45);
+ return null;
+ }
+
+ setStatus("Submitting data...");
+ setProgress(60);
+
+
+ ArrayList uploadList = gui.getUploadList(table, cfg);
+
+
+ int[] selectedForUpload = new int[uploadList.size()];
+ for (int i = 0; i< uploadList.size(); i++) {
+ selectedForUpload[i] = uploadList.get(i);
+ System.out.println(uploadList.get(i));
+ }
+ EcdcFullUploadWorker uploadWorker = gui.getFullUploadWorker(importConfig.getSubject(), selectedForUpload);
+
+ if (isStopped()) {
+ log("Job interrupted, exiting");
+ setStatus("Error, job interrupted");
+ setProgress(70);
+ return null;
+ }
+
+ // If the worker was successfully created, run it and wait for the job to finish
+ if (uploadWorker!=null) {
+ setProgress(65);
+ uploadWorker.execute();
+ boolean done=false;
+ while (!done) {
+ try {
+ Thread.sleep(5000);
+ } catch (InterruptedException e) {
+ }
+
+ // Check if this job was interrupted
+ if (uploadWorker.isStopped()) {
+ log("Job interrupted, exiting");
+ setStatus("Error, job interrupted");
+ setProgress(70);
+ return null;
+ }
+ if (uploadWorker.isDone()) {
+ done=true;
+ }
+ }
+ } else {
+ log("GUI returned null FullUploadWorker, exiting");
+ setStatus("Error, null FullUploadWorker");
+ setProgress(70);
+ return null;
+ }
+ log("Job finished");
+ setStatus("Automated import and upload finished.");
+ setProgress(100);
+ return null;
+ }
+
+}
diff --git a/src/eu/europa/ecdc/enauploader/EcdcFullUploadWorker.java b/src/eu/europa/ecdc/enauploader/EcdcFullUploadWorker.java
new file mode 100644
index 0000000..a68d314
--- /dev/null
+++ b/src/eu/europa/ecdc/enauploader/EcdcFullUploadWorker.java
@@ -0,0 +1,284 @@
+package eu.europa.ecdc.enauploader;
+
+public class EcdcFullUploadWorker extends EcdcJob {
+
+ // Rows to submit
+ private int[] selectedRows;
+
+ // Mandatory constructor
+ EcdcFullUploadWorker(EcdcUploaderGUI gui, TessyBatch batch, String name, UploadConfig cfg, String[] headers,
+ String[][] data) {
+ super(gui, batch, name, cfg, headers, data);
+
+ }
+
+ // Method for settings the rows to submit, should be called before starting the job
+ public void setRows(int[] selected) {
+ selectedRows = selected;
+
+ }
+
+ // Main routine for this EcdcJob
+ @Override
+ protected Object doInBackground() {
+
+ log("Submission to multiple systems started.");
+ setTitle("Submission to multiple systems");
+ setStatus("Initializing");
+ setProgress(2);
+
+ if (selectedRows==null || selectedRows.length==0) {
+ setStatus("Nothing to do, exiting.");
+ setProgress(100);
+ return null;
+ }
+
+ // This section is for submitting to ENA
+ if (cfg.isSubmitEna()) {
+ setStatus("Submitting to ENA");
+ setProgress(5);
+ log("Launching ENA job");
+
+ // Create EnaSubmissionWorker
+ EnaSubmissionWorker enaWorker = gui.getEnaSubmissionWorker(name, selectedRows);
+
+ // Check if this job was interrupted
+ if (isStopped()) {
+ log("Job interrupted, exiting");
+ setStatus("Error, job interrupted");
+ setProgress(30);
+ return null;
+ }
+
+ // If the worker was successfully created, run it and wait for the job to finish
+ if (enaWorker!=null) {
+ enaWorker.execute();
+ boolean done=false;
+ while (!done) {
+ try {
+ Thread.sleep(5000);
+ } catch (InterruptedException e) {
+ }
+
+ // Check if this job was interrupted
+ if (enaWorker.isStopped()) {
+ log("Job interrupted, exiting");
+ setStatus("Error, job interrupted");
+ setProgress(30);
+ return null;
+ }
+ if (enaWorker.isDone()) {
+ done=true;
+ }
+ }
+ } else {
+ log("GUI returned null enaWorker, continuing without ENA");
+ }
+
+
+ }
+
+ // This section is for submitting to TESSy
+ if (cfg.isSubmitTessy()) {
+ // Check if this job was interrupted
+ if (isStopped()) {
+ log("Job interrupted, exiting");
+ setStatus("Error, job interrupted");
+ setProgress(30);
+ return null;
+ }
+ setStatus("Testing at TESSy");
+ setProgress(30);
+ log("TESSy test job");
+
+ // Create and run a job for testing at TESSy
+ EcdcTessyCreateAndTestWorker tessyTestWorker = gui.getEcdcTessyTestWorker(name, selectedRows);
+ if (tessyTestWorker!=null) {
+ tessyTestWorker.execute();
+ } else {
+ setStatus("Error, no valied entries selected");
+ setProgress(35);
+ log("GUI returned null tessyTestWorker, likely an error in the isolate table");
+ return null;
+ }
+
+ // Wait for job to finish
+ boolean done=false;
+ while (!done) {
+ try {
+ Thread.sleep(5000);
+ } catch (InterruptedException e) {
+ }
+
+ // Check if this job was interrupted
+ if (tessyTestWorker.isStopped()) {
+ log("Job interrupted, exiting");
+ setStatus("Error, job interrupted");
+ setProgress(35);
+ return null;
+ }
+ if (tessyTestWorker.isDone()) {
+ done=true;
+ }
+ }
+
+ // Check the validation results and decide whether to continue
+ batch = tessyTestWorker.getBatch();
+ String batchId = batch.getBatchId();
+ if (!batch.passedValidation()) {
+ log("Batch did not pass validation, quitting");
+ setStatus("Error, the batch did not pass TESSy validation");
+ setProgress(35);
+ return null;
+ }
+
+ // Check if this job was interrupted
+ if (isStopped()) {
+ log("Job interrupted, exiting");
+ setStatus("Error, job interrupted");
+ setProgress(40);
+ return null;
+ }
+
+ // Create and run a job for uploading to TESSy
+ setStatus("Uploading to TESSy");
+ setProgress(40);
+ log("TESSy upload job");
+ EcdcTessyUploadWorker tessyUploadWorker = gui.getEcdcTessyUploadWorker(batchId);
+ if (tessyUploadWorker!=null) {
+ tessyUploadWorker.execute();
+ } else {
+ log("GUI returned null tessyUploadWorker, likely an error in the isolate table");
+ return null;
+ }
+
+ // Wait for job to finish
+ done=false;
+ while (!done) {
+ try {
+ Thread.sleep(5000);
+ } catch (InterruptedException e) {
+ }
+
+ // Check if this job was interrupted
+ // with batches going out of synch between the app and TESSy
+ if (tessyUploadWorker.isStopped()) {
+ log("Job interrupted, exiting");
+ setStatus("Error, job interrupted");
+ setProgress(45);
+ return null;
+ }
+ if (tessyUploadWorker.isDone()) {
+ done=true;
+ }
+ }
+
+ // Check the validation results and decide whether to continue. This should pretty much never fail as the batch has been tested.
+ // If the batch somehow fails at this stage, it must be rejected manually before retrying.
+ batch = tessyUploadWorker.getBatch();
+ if (!batch.passedValidation()) {
+ log("Batch did not pass validation after Upload, quitting");
+ setStatus("Error, the batch did not pass TESSy validation");
+ setProgress(45);
+ return null;
+ }
+
+ // Check if this job was interrupted
+ // with batches going out of synch between the app and TESSy
+ if (isStopped()) {
+ log("Job interrupted, exiting");
+ setStatus("Error, job interrupted");
+ setProgress(60);
+ return null;
+ }
+ setStatus("Approving in TESSy");
+ setProgress(60);
+ log("TESSy approve job");
+ EcdcTessyApprovalWorker tessyApprovalWorker = gui.getEcdcTessyApprovalWorker(batchId);
+ if (tessyApprovalWorker!=null) {
+ tessyApprovalWorker.execute();
+ } else {
+ log("GUI returned null tessyApprovalWorker, likely an error in the isolate table");
+ return null;
+ }
+ done=false;
+ while (!done) {
+ try {
+ Thread.sleep(5000);
+ } catch (InterruptedException e) {
+ }
+
+ // Check if this job was interrupted
+ // with batches going out of synch between the app and TESSy
+ if (tessyApprovalWorker.isDone()) {
+ done=true;
+ }
+ }
+
+ // Check if batch was actually approved
+ batch = tessyApprovalWorker.getBatch();
+ if (!batch.getState().equals("APPROVED")) {
+ log("Batch Approval failed, quitting");
+ setStatus("Error, batch approval failed");
+ setProgress(65);
+ return null;
+ }
+
+
+ }
+
+ // This section is for submission to SFTP
+ if (cfg.isSubmitFtp()) {
+
+ // Check if this job was interrupted
+ if (isStopped()) {
+ log("Job interrupted, exiting");
+ setStatus("Error, job interrupted");
+ setProgress(75);
+ return null;
+ }
+
+ setStatus("Submitting to ECDC SFTP");
+ setProgress(75);
+ log("Launching SFTP UPLOAD job");
+
+ // Create and run a job for submitting to SFTP
+ EcdcSftpUploadWorker sftpWorker = gui.getSftpWorker(name, selectedRows);
+ if (sftpWorker!=null) {
+ sftpWorker.execute();
+ } else {
+ log("GUI returned null sftpWorker, likely an error in the isolate table");
+ return null;
+ }
+
+ // Wait for the job to finish
+ boolean done=false;
+ while (!done) {
+ try {
+ Thread.sleep(5000);
+ } catch (InterruptedException e) {
+ }
+
+ // Check if this job was interrupted
+ if (sftpWorker.isStopped()) {
+ log("Job interrupted, exiting");
+ setStatus("Error, job interrupted");
+ setProgress(80);
+ return null;
+ }
+ if (sftpWorker.isDone()) {
+ done=true;
+ }
+ }
+
+
+ }
+ setStatus("Finished!");
+ setProgress(100);
+
+ return null;
+ }
+
+
+
+}
diff --git a/src/eu/europa/ecdc/enauploader/EcdcImportWorker.java b/src/eu/europa/ecdc/enauploader/EcdcImportWorker.java
new file mode 100644
index 0000000..ef091b0
--- /dev/null
+++ b/src/eu/europa/ecdc/enauploader/EcdcImportWorker.java
@@ -0,0 +1,268 @@
+package eu.europa.ecdc.enauploader;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+import javax.swing.JTable;
+import javax.swing.table.DefaultTableModel;
+
+public class EcdcImportWorker extends EcdcJob {
+
+ private JTable table;
+ private ImportConfig importConfig;
+ private File importFile;
+
+ EcdcImportWorker(EcdcUploaderGUI gui, TessyBatch batch, String name, UploadConfig cfg, String[] headers,
+ String[][] data) {
+ super(gui, batch, name, cfg, headers, data);
+
+ }
+
+
+ public void setImportConfig (ImportConfig cfg) {
+ this.importConfig = cfg;
+ }
+
+ public void setTable (JTable table) {
+ this.table = table;
+ }
+
+ @Override
+ protected Object doInBackground() throws Exception {
+
+ if (importFile==null) {
+ importFile = new File(importConfig.getDatafile());
+ }
+
+ setTitle("Importing data for: "+name);
+ setStatus("Performing import");
+ setProgress(10);
+
+ doImport(table, importConfig, this);
+
+ setStatus("Import finished.");
+ setProgress(100);
+
+ return null;
+ }
+
+
+ // Method for import using a ImportConfig, will select the source from the config parameters
+ private ArrayList doImport(JTable table, ImportConfig cfg, EcdcJob job) {
+
+ if (job!=null) {
+ job.log("Starting import...");
+ }
+
+ ArrayList importedRows = new ArrayList();
+ ArrayList oldEntries = new ArrayList();
+
+ // Read the existing entries
+ for (int i = 0;i importFields = new ArrayList();
+ ArrayList tableIndices = new ArrayList();
+ HashMap fixed = new HashMap();
+
+ // Get any constant field values from the config, these will not be imported but rather set to the fixed value
+ for (String k: cfg.getConstants().keySet()) {
+ String value = cfg.getConstants().get(k);
+ for (int i = 0;i data=null;
+
+ if (job!=null) {
+ job.log("Checking import source type");
+ }
+
+ // Choose import source type and call the corresponding import method
+ switch (cfg.getImportType()) {
+ case ImportConfig.IMPORT_SQL:
+ if (job!=null) {
+ job.log("SQL");
+ }
+ data = ImportTools.importSql(cfg,oldEntries, importFields, job);
+ break;
+
+ case ImportConfig.IMPORT_SQLITE:
+ if (job!=null) {
+ job.log("SQLite");
+ }
+ data = ImportTools.importSqlite(cfg,oldEntries, importFields, job);
+ break;
+
+ case ImportConfig.IMPORT_CSV:
+ if (job!=null) {
+ job.log("CSV");
+ }
+
+ data = ImportTools.importCsv(cfg, oldEntries, importFields, job, importFile);
+ break;
+
+ case ImportConfig.IMPORT_EXCEL:
+ if (job!=null) {
+ job.log("Excel");
+ }
+ data = ImportTools.importExcel(cfg, oldEntries, importFields, job, importFile);
+ break;
+
+ }
+
+
+ if (data==null) {
+ if (job!=null) {
+ job.log("Null data returned by import routine");
+ }
+ return importedRows;
+ }
+
+ // Map the values
+ if (job!=null) {
+ job.log("Mapping values");
+ }
+
+ data = ImportTools.mapValues(data,importFields,tableIndices,cfg.getValueMap());
+
+
+ DefaultTableModel model =(DefaultTableModel)table.getModel();
+
+ // Make a HashMap from RecordId to row number for the existing entries
+ HashMap oldIds = new HashMap();
+ for (int i = 0;i {
+
+ // This abstract class should be used for background jobs launched by the Ecdc upload app
+ // The constructor automatically adds the job to the GUI's job handler
+
+ // doInbackground() must be implemented by the class inheriting this class, that is where the actual code goes
+ //
+ // A constructor also needs to be implemented, similar to the one below:
+ // EcdcSftpUploadWorker(EcdcUploaderGUI gui, TessyBatch batch, String name, UploadConfig cfg, String[] headers, String[][] data) {
+ // super(gui, batch, name, cfg, headers, data);
+ // }
+ //
+ // To set the status shown in the job manager, these three methods are used, the GUI is only updated on setProgress(int), so do that last.
+ // setTitle(String title);
+ // setStatus(String status);
+ // setProgress(int progress); progress goes from 0 (just started) to 100 (finished)
+ //
+ // To add to the job log, use the method:
+ // log(String message)
+ //
+
+ protected String name;
+ protected UploadConfig cfg;
+ protected String[] headers;
+ protected String[][] data;
+ protected int batchId;
+ protected EcdcUploaderGUI gui;
+ protected TessyBatch batch;
+ private String status;
+ private String title;
+ private String logText;
+ private SimpleDateFormat dateFormat;
+ private boolean stopped = false;
+ private String id;
+
+ EcdcJob (EcdcUploaderGUI gui, TessyBatch batch, String name, UploadConfig cfg, String[] headers,String[][] data) {
+ this.dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+ this.logText = "";
+ this.gui = gui;
+ this.batch = batch;
+ this.name = name;
+ this.cfg = cfg;
+ this.headers = headers;
+ this.data = data;
+ gui.getJobHandler().addJob(this);
+ this.addPropertyChangeListener(gui.getJobHandler());
+ this.setProgress(1);
+ SimpleDateFormat logDateFormat = new SimpleDateFormat("yyyy-MM-dd_HHmmss_SS");
+ id = logDateFormat.format(new Date());
+ }
+
+ public boolean isStopped() {
+ return stopped;
+ }
+
+ public String getStatus() {
+ return status;
+ }
+
+ public void setStatus(String status) {
+ this.status = status;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ public void log(String text){
+ if (!logText.equals("")) {
+ logText = logText+"\n";
+ }
+ logText=logText+"["+dateFormat.format(new Date())+"] "+text;
+ }
+
+ public String getLogText() {
+ return logText;
+ }
+
+ public TessyBatch getBatch() {
+ return batch;
+ }
+
+ public void stopJob(boolean b) {
+ stopped = true;
+ }
+
+
+
+ public void done() {
+
+ File logDir = new File("./logs");
+ if (!logDir.exists()) {
+ logDir.mkdirs();
+ }
+ File logFile = new File(logDir.toString()+"/"+id+".log");
+
+ log("Writing logfile: "+logFile.toString());
+
+ try {
+ BufferedWriter bw = new BufferedWriter(new FileWriter(logFile));
+ bw.write(logText);
+ bw.close();
+ } catch(IOException e) {
+ log("Failed to write logfile to disk.");
+ }
+
+
+ }
+
+}
diff --git a/src/eu/europa/ecdc/enauploader/EcdcJobHandler.java b/src/eu/europa/ecdc/enauploader/EcdcJobHandler.java
new file mode 100644
index 0000000..a9ffb50
--- /dev/null
+++ b/src/eu/europa/ecdc/enauploader/EcdcJobHandler.java
@@ -0,0 +1,200 @@
+package eu.europa.ecdc.enauploader;
+
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.image.BufferedImage;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.io.InputStream;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+
+import javax.imageio.ImageIO;
+import javax.swing.BoxLayout;
+import javax.swing.JButton;
+import javax.swing.JFrame;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTextArea;
+
+//This class is the GUI-component and actual job manager for the Ecdc upload app
+public class EcdcJobHandler extends JFrame implements PropertyChangeListener, ActionListener{
+
+ private static final long serialVersionUID = 6686812811438715750L;
+
+
+ JPanel mainPanel;
+ private JPanel jobPanel;
+ private LinkedHashMap jobs;
+ private LinkedHashMap panels;
+
+
+ EcdcJobHandler() {
+
+ // Init UI components
+ mainPanel = new JPanel(new BorderLayout());
+
+ JButton clearButton = new JButton("Clear all finished jobs");
+ clearButton.setActionCommand("clear");
+ clearButton.addActionListener(this);
+ JPanel clearPanel = new JPanel();
+ clearPanel.add(clearButton);
+
+ mainPanel.add(clearPanel,BorderLayout.NORTH);
+
+ jobPanel = new JPanel();
+ jobs = new LinkedHashMap();
+ panels = new LinkedHashMap();
+
+ setSize(800,600);
+ mainPanel.setSize(800,600);
+ jobPanel.setSize(800,600);
+ jobPanel.setLayout(new BoxLayout(jobPanel, BoxLayout.PAGE_AXIS));
+ mainPanel.add(jobPanel,BorderLayout.CENTER);
+ this.add(mainPanel);
+ setTitle("ECDC WGS upload app - Job handler");
+ this.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
+ try {
+ String imagePath = "media/ECDC2.png";
+ InputStream imgStream = UploadConfigGUI.class.getResourceAsStream(imagePath );
+ BufferedImage myImg;
+ myImg = ImageIO.read(imgStream);
+ setIconImage(myImg);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ pack();
+ }
+
+
+ // Add a new job. This is called by the EcdcJob constructor and does not explicitly need to be called
+ public void addJob(EcdcJob job) {
+ EcdcJobPanel panel = new EcdcJobPanel(job.toString(),this);
+ jobs.put(job,panel);
+ panels.put(panel,job);
+ jobPanel.add(panel);
+ job.log("Added job to job manager");
+
+ // Update UI and make it pop to the front
+ pack();
+ jobPanel.repaint();
+ setVisible(true);
+ toFront();
+ }
+
+ // Remove job from handler
+ public void removeJob(EcdcJobPanel panel) {
+
+ EcdcJob job = panels.get(panel);
+ if (job.isDone()) {
+ jobPanel.remove(panel);
+ jobs.remove(job);
+ panels.remove(panel);
+ }
+ if (panels.isEmpty()) {
+ setVisible(false);
+ }
+ pack();
+ repaint();
+ }
+
+ // Try to interrupt job
+ public void cancelJob(EcdcJobPanel panel) {
+ EcdcJob job = panels.get(panel);
+
+ // If the job is already gone, show that it is cancelled
+ if (job==null) {
+ panel.cancel();
+ return;
+ }
+
+ // Try to cancel the job, if successful, show that it is cancelled
+ // Note that jobs may be impossible to cancel at some stages, the individual EcdcJob
+ // implementations have to handle the signal
+ job.stopJob(false);
+ if (job.isDone()) {
+ panel.cancel();
+ }
+
+ }
+
+ // When any job is updated, update UI
+ @Override
+ public void propertyChange(PropertyChangeEvent evt) {
+
+ // Get the source job
+ EcdcJob source = (EcdcJob) evt.getSource();
+ System.out.println("job event! "+source.toString());
+
+ // Get the visual job panel
+ EcdcJobPanel panel = jobs.get(source);
+ if (panel==null) {
+ return;
+ }
+
+ // If job made progress, update progress
+ if (evt.getPropertyName().equals("progress")) {
+ int intVal = (Integer)evt.getNewValue();
+ panel.setProgress(intVal);
+ }
+
+ // Set title and status
+ String status = source.getStatus();
+ String title = source.getTitle();
+ panel.setLabel(status);
+ if (title!=null) {
+ panel.setTitle(title);
+ }
+
+ // Check if job finished, if so, update panel reflecting this information
+ if (source.isDone()) {
+ panel.setDone();
+ }
+
+ }
+
+ // This method opens a window where the log for a certain job is shown.
+ public void showLog(String title, EcdcJobPanel panel) {
+ EcdcJob job = panels.get(panel);
+ String logText = job.getLogText();
+
+ JFrame f = new JFrame("Log for "+title);
+ JPanel logPanel = new JPanel();
+
+ f.getContentPane().add(logPanel, "Center");
+ JTextArea ta = new JTextArea(logText);
+
+ JScrollPane scroller = new JScrollPane(ta);
+ scroller.setPreferredSize(new Dimension(1200, 800));
+ logPanel.add(scroller);
+
+ try {
+ String imagePath = "media/ECDC2.png";
+ InputStream imgStream = UploadConfigGUI.class.getResourceAsStream(imagePath );
+ BufferedImage myImg;
+ myImg = ImageIO.read(imgStream);
+ f.setIconImage(myImg);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
+ f.setSize(1300, 900);
+ f.setVisible(true);
+ }
+
+ // Remove finished jobs from UI
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ LinkedHashSet jobPanels = new LinkedHashSet(panels.keySet());
+ for (EcdcJobPanel p : jobPanels) {
+ removeJob(p);
+ }
+ }
+
+
+
+
+}
diff --git a/src/eu/europa/ecdc/enauploader/EcdcJobPanel.java b/src/eu/europa/ecdc/enauploader/EcdcJobPanel.java
new file mode 100644
index 0000000..8375bbf
--- /dev/null
+++ b/src/eu/europa/ecdc/enauploader/EcdcJobPanel.java
@@ -0,0 +1,119 @@
+package eu.europa.ecdc.enauploader;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+import javax.swing.BorderFactory;
+import javax.swing.JButton;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JProgressBar;
+import javax.swing.border.TitledBorder;
+
+public class EcdcJobPanel extends JPanel implements ActionListener {
+
+ private static final long serialVersionUID = 8549159337996248380L;
+ private JProgressBar bar;
+ private JLabel label;
+ private EcdcJobHandler handler;
+ private JButton closeButton;
+ private JButton logButton;
+
+ EcdcJobPanel(String title, EcdcJobHandler handler) {
+
+ // Init UI components
+ this.handler=handler;
+ setBorder(BorderFactory.createTitledBorder(title));
+ label = new JLabel(title);
+ bar = new JProgressBar();
+ bar.setMaximum(100);
+ bar.setMinimum(0);
+ bar.setValue(0);
+ add(label);
+ add(bar);
+ setSize(100,500);
+ closeButton = new JButton("Cancel");
+ closeButton.setActionCommand("Cancel");
+ closeButton.addActionListener(this);
+ add(closeButton);
+ logButton = new JButton("Show log");
+ logButton.setActionCommand("log");
+ logButton.addActionListener(this);
+ add(logButton);
+ }
+
+ // Set progressbar, from 0 to 100
+ public void setProgress(int intVal) {
+ bar.setValue(intVal);
+
+ // If progress at max, change button text and action
+ if (intVal==100) {
+ setDone();
+ }
+ repaint();
+ }
+
+ // Change action and text on button to reflect that the job is finished
+ public void setDone() {
+ closeButton.setActionCommand("Remove");
+ closeButton.setText("Remove");
+ repaint();
+ }
+
+ // Update the text next to the progressbar
+ public void setLabel(String l) {
+ if (l==null) {
+ return;
+ }
+ label.setText(l);
+ if (l.startsWith("Error")) {
+ closeButton.setText("Remove");
+ closeButton.setActionCommand("Remove");
+ }
+ repaint();
+ }
+
+ // Update the title of the job
+ public void setTitle(String l) {
+ TitledBorder b = (TitledBorder)this.getBorder();
+ b.setTitle(l);
+ }
+
+
+ // Reflect visually that a job has been cancelled
+ public void cancel() {
+ closeButton.setText("Remove");
+ closeButton.setActionCommand("Remove");
+ setLabel("Cancelled");
+ repaint();
+ }
+
+ // Action for the buttons
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ String cmd = e.getActionCommand();
+ action(cmd);
+ }
+
+
+ // The various actions possible for the buttons
+ // The handler should perform all operations on jobs and then call this class for updating UI
+ public void action(String cmd) {
+
+ // Cancel the job
+ if (cmd.equals("Cancel")) {
+ handler.cancelJob(this);
+
+ // Remove the job
+ } else if (cmd.equals("Remove")) {
+ handler.removeJob(this);
+
+ // Open log
+ } else if (cmd.equals("log")) {
+ String t = ((TitledBorder)this.getBorder()).getTitle();
+ handler.showLog(t,this);
+
+ }
+ }
+
+}
diff --git a/src/eu/europa/ecdc/enauploader/EcdcLinkWorker.java b/src/eu/europa/ecdc/enauploader/EcdcLinkWorker.java
new file mode 100644
index 0000000..8efab86
--- /dev/null
+++ b/src/eu/europa/ecdc/enauploader/EcdcLinkWorker.java
@@ -0,0 +1,224 @@
+package eu.europa.ecdc.enauploader;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+
+import javax.swing.JOptionPane;
+import javax.swing.JTable;
+
+public class EcdcLinkWorker extends EcdcJob {
+
+ private JTable table;
+ private int[] selected;
+ private String READ_FILES_COLUMN;
+ private String ASSEMBLY_FILE_COLUMN;
+ private ArrayList linkedRows;
+ private String BASE_FILES_COLUMN;
+
+
+ EcdcLinkWorker(EcdcUploaderGUI gui, TessyBatch batch, String name, UploadConfig cfg, String[] headers,
+ String[][] data) {
+ super(gui, batch, name, cfg, headers, data);
+ selected = null;
+ }
+
+ public void setTable (JTable table) {
+ this.table = table;
+ }
+
+ public void setSelected (int[] selected) {
+ this.selected = selected;
+ }
+
+ public void setColumnNames(String baseName, String rawName, String assemblyName) {
+ BASE_FILES_COLUMN = baseName;
+ READ_FILES_COLUMN = rawName;
+ ASSEMBLY_FILE_COLUMN = assemblyName;
+ }
+
+
+ @Override
+ protected Object doInBackground() throws Exception {
+
+ setTitle("Linking sequence files");
+ setStatus("Initializing");
+ setProgress(5);
+
+ // Get the selected table if not supplied
+
+ // use selected rows if not supplied
+ if (selected==null) {
+ selected = table.getSelectedRows();
+ }
+
+ if (cfg == null) {
+ log("Error, no config found for "+name);
+ setStatus("Initialization error");
+ setProgress(10);
+ return null;
+ }
+
+
+
+ String rawDir = cfg.getRawdataDir();
+ String assemblyDir = cfg.getAssemblyDir();
+ String rawDelim = cfg.getRawdataDelimiter();
+ String assemblyDelim = cfg.getAssemblyDelimiter();
+
+ // Check if data directories are supplied in the config
+ if (rawDir.equals("") && assemblyDir.equals("")) {
+ log("At least one of 'Assembly directory' or 'Raw reads directory' must be defined in the configuration.");
+ setStatus("Initialization error");
+ setProgress(10);
+ return null;
+ }
+
+
+
+ // Init variables
+ linkedRows = new ArrayList();
+
+ int numberLinkedRaw = 0;
+ int numberLinkedAssembly = 0;
+
+ setStatus("Reading raw data directory");
+ log("Reading raw data directory: "+rawDir);
+ setProgress(10);
+ // List the raw data directory recursively
+ ArrayList foundFiles = new ArrayList();
+ File rawDirFile = new File(rawDir);
+ if (!rawDir.equals("") && rawDirFile.exists() && rawDirFile.isDirectory()) {
+ try {
+ foundFiles = EcdcUtils.search(new File(rawDir), foundFiles);
+ } catch (IOException e) {
+ log(e.getMessage());
+ setStatus("IO Error");
+ setProgress(12);
+ }
+ } else {
+ log("No raw reads directory found. Skipping import of raw reads.");
+ }
+
+ setStatus("Reading assembly data directory");
+ log("Reading assembly data directory: "+assemblyDir);
+ setProgress(35);
+ ArrayList foundFilesAssembly;
+ if (!assemblyDir.equals(rawDir)) {
+ // List the assembly data directory recursively
+ foundFilesAssembly = new ArrayList();
+ File assemblyDirFile = new File(assemblyDir);
+ if (!assemblyDir.equals("") && assemblyDirFile.exists() && assemblyDirFile.isDirectory()) {
+ try {
+ foundFilesAssembly = EcdcUtils.search(new File(assemblyDir), foundFilesAssembly);
+ } catch (IOException e) {
+ log(e.getMessage());
+ setStatus("IO Error");
+ setProgress(37);
+ }
+ } else {
+ log("No assembly directory found. Skipping import of assemblies.");
+ }
+ } else {
+ log("Assembly directory same as raw read directory, reusing index.");
+ foundFilesAssembly = (ArrayList)foundFiles.clone();
+ }
+
+ // Init column indices
+ int baseCol = gui.getColumn(table,BASE_FILES_COLUMN);
+ int fileCol = gui.getColumn(table,READ_FILES_COLUMN);
+ int assemblyCol = gui.getColumn(table,ASSEMBLY_FILE_COLUMN);
+ log("Raw reads table column: "+Integer.toString(fileCol));
+ log("Assembly table column: "+Integer.toString(assemblyCol));
+
+ setStatus("Matching entries with files");
+ log("Matching entries with files");
+ setProgress(60);
+ // Iterate through the table rows
+ log("Iterating over "+Integer.toString(foundFiles.size())+" files in reads direcotyr.");
+ log("Iterating over "+Integer.toString(foundFilesAssembly.size())+" files in assembly directory.");
+ log("Iterating over "+Integer.toString(selected.length)+" entries.");
+ for (int i : selected) {
+
+ String id;
+ String baseVal = (String)table.getValueAt(i, baseCol);
+ if (baseVal==null || baseVal.equals("")) {
+ String recordId = (String)table.getValueAt(i, 0);
+ id = recordId;
+ } else {
+ id = baseVal;
+ }
+
+
+
+
+ String files = (String)table.getValueAt(i, fileCol);
+ String filesAssembly = (String)table.getValueAt(i, assemblyCol);
+
+ // Skip rows missing RecordId
+ if (id == null || id.equals("")) {
+ continue;
+ }
+
+ // If the raw read links are empty, try to find links
+
+ String pattern = id+rawDelim+".*[.]?fastq[.]?g?z?";
+ String fileStr = "";
+
+ for (File f : foundFiles) {
+ if (!f.getName().matches(pattern)) {
+ continue;
+ }
+ if (!fileStr.equals("")) {
+ fileStr = fileStr + ";";
+ }
+ fileStr = fileStr + f.toString();
+ }
+
+ if (!fileStr.equals("") && (files==null || files.equals("") || !files.equals(fileStr))) {
+ linkedRows.add(i);
+ table.setValueAt(fileStr,i, fileCol);
+ numberLinkedRaw++;
+ }
+
+
+ setProgress(80);
+ // If the assembly links are empty, try to find links
+
+ pattern = id+assemblyDelim+".*[.]?fasta[.]?g?z?";
+
+ fileStr = "";
+
+ for (File f : foundFilesAssembly) {
+
+ if (!f.getName().matches(pattern)) {
+ continue;
+ }
+ if (!fileStr.equals("")) {
+ fileStr = fileStr + ";";
+ }
+ fileStr = fileStr + f.toString();
+ }
+ if (!fileStr.equals("") && (filesAssembly==null || filesAssembly.equals("") || !filesAssembly.equals(fileStr))) {
+ table.setValueAt(fileStr,i, assemblyCol);
+ if (linkedRows.indexOf(i)==-1) {
+ linkedRows.add(i);
+ }
+ numberLinkedAssembly++;
+ }
+ }
+
+
+
+
+ log("Finished, "+Integer.toString(numberLinkedRaw)+"(reads) "+Integer.toString(numberLinkedAssembly)+"(assembly) "+" linked files");
+ setStatus("Finished, "+Integer.toString(numberLinkedRaw)+"(reads) "+Integer.toString(numberLinkedAssembly)+"(assembly) "+" linked files");
+ setProgress(100);
+ return null;
+ }
+
+ public ArrayList getLinkedEntries() {
+ return linkedRows;
+ }
+
+}
diff --git a/src/eu/europa/ecdc/enauploader/EcdcSftpUploadWorker.java b/src/eu/europa/ecdc/enauploader/EcdcSftpUploadWorker.java
new file mode 100644
index 0000000..59883cd
--- /dev/null
+++ b/src/eu/europa/ecdc/enauploader/EcdcSftpUploadWorker.java
@@ -0,0 +1,412 @@
+package eu.europa.ecdc.enauploader;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+
+import org.apache.commons.vfs2.FileObject;
+import org.apache.commons.vfs2.FileSystemException;
+import org.apache.commons.vfs2.FileSystemManager;
+import org.apache.commons.vfs2.FileSystemOptions;
+import org.apache.commons.vfs2.Selectors;
+import org.apache.commons.vfs2.VFS;
+import org.apache.commons.vfs2.provider.sftp.SftpFileSystemConfigBuilder;
+import org.jdom2.Attribute;
+import org.jdom2.Document;
+import org.jdom2.Element;
+import org.jdom2.output.Format;
+import org.jdom2.output.XMLOutputter;
+
+public class EcdcSftpUploadWorker extends EcdcJob {
+
+ private OutputHandler outputHandler;
+ private int ftpColumn;
+ private static final int WAIT_RETRY = 10;
+ private static final int MAX_TRIES = 5;
+ private static final String XML_API_VERSION = "1.0.1";
+
+ // Mandatory constructor
+ EcdcSftpUploadWorker(EcdcUploaderGUI gui, TessyBatch batch, String name, UploadConfig cfg, String[] headers,
+ String[][] data) {
+ super(gui, batch, name, cfg, headers, data);
+
+ }
+
+ // Set the handler for output.
+ // TODO: This API is only used for this class, harmonisation for all EcDCJobs would be nice
+ public void setOutputHandler(OutputHandler handler) {
+ this.outputHandler = handler;
+ }
+
+ // Set the table column for output
+ public void setFtpColumn(int col) {
+ ftpColumn = col;
+ }
+
+
+ // Main routine for this job
+ @Override
+ protected Object doInBackground() {
+
+ // Check if cancelled
+ if (isStopped()) {
+ log("Process cancelled");
+ return null;
+ }
+
+ log("Uploading raw reads through SFTP");
+ setTitle("Uploading reads using SFTP");
+ setStatus("Connecting to SFTP host");
+ setProgress(5);
+
+ // Init parameters from config
+ String sftpLogin = cfg.getSftpLogin();
+ String sftpHost = cfg.getSftpHost();
+ String sftpPass = new String(cfg.getSftpPass());
+ String sftpPath = cfg.getSftpPath();
+ String tmpPath = cfg.getTmpPath();
+ File dataDir = new File(cfg.getRawdataDir());
+ if (!dataDir.exists()) {
+ log("Data directory "+dataDir.toString()+" does not exist.");
+ setStatus("Error, data dir not found");
+ setProgress(10);
+ return null;
+ }
+
+ int step = 80/data.length;
+
+ // Iterate over the entries to submit
+ int total = data.length;
+ int fail = 0;
+
+ if (total==0) {
+ setStatus("Error, no valid entries selected");
+ setProgress(20);
+ return null;
+ }
+
+ ISOLATE: for (int i = 0; i files = new ArrayList();
+ ArrayList tmpFiles = new ArrayList();
+ for (String fn: fileNames) {
+ File file = new File(fn);
+ if (file.exists()) {
+
+ if (fn.toLowerCase().endsWith(".fastq.gz") || fn.toLowerCase().endsWith(".fastq") || fn.toLowerCase().endsWith(".fq") || fn.toLowerCase().endsWith(".fq.gz")|| fn.toLowerCase().endsWith(".fasta") || fn.toLowerCase().endsWith(".fa")) {
+ if (fn.endsWith(".gz") || fn.endsWith(".fasta") || fn.endsWith(".fa")) {
+ files.add(file);
+ log("Record "+recordId+", found file: "+fn);
+ } else {
+ log("Record "+recordId+", found uncompressed file: "+fn);
+ String tmpFilePath = tmpPath+"/"+file.getName()+".gz";
+ File tmpFile = new File(tmpFilePath);
+ log("Compressing "+fn+" to "+tmpFilePath);
+ EcdcUtils.compressGzipFile(file,tmpFile);
+ tmpFiles.add(tmpFile);
+ files.add(tmpFile);
+ }
+ }
+
+
+ } else {
+ log("Error, for record "+recordId+", file: "+fn+" does not exist.");
+ fail++;
+ continue ISOLATE;
+ }
+ }
+
+ if (files.isEmpty()) {
+ log("No raw data files found for "+recordId+", skipping.");
+ fail++;
+ continue ISOLATE;
+ } else {
+ log(Integer.toString(files.size()) + " raw data files found for "+recordId);
+ }
+
+ String remotePath = sftpPath+"/"+cfg.getTessySubject();
+
+ int count = 0;
+ int step2 = step/files.size();
+
+
+ FileSystemManager fsManager;
+ FileSystemOptions fsOptions;
+ try {
+ fsOptions = new FileSystemOptions();
+ SftpFileSystemConfigBuilder.getInstance().setStrictHostKeyChecking(fsOptions, "no");
+ SftpFileSystemConfigBuilder.getInstance().setTimeout(fsOptions, 30000);
+ fsManager = VFS.getManager();
+ } catch (Exception e) {
+ log("Failed to initialize SFTP filesystem, quitting.");
+ fail++;
+ continue ISOLATE;
+ }
+
+ // Create a MD5 digest of concatenated data provider and TESSy GUID
+ // This is used to anonymize the file names for the SFTP
+ String baseName = org.apache.commons.codec.digest.DigestUtils.md5Hex(cfg.getTessyProvider()+"_"+recordId);
+ String xmlName = baseName+".xml";
+ String xmlFile = tmpPath+"/"+xmlName;
+
+ // Init data structures for the info that is going into the XML waybill
+ HashMap newFileNames = new HashMap();
+ ArrayList fileNamesXml = new ArrayList();
+ ArrayList checksumsXml = new ArrayList();
+
+ int fileCount = 1;
+ // Iterate over the files
+ for (File f : files) {
+ String newName;
+ // Anonymize and standardise filename
+ // TODO: Not great to have IONTORRENT hardcoded
+ if (f.getName().endsWith(".fa") || f.getName().endsWith(".fasta")) {
+ newName = baseName+".fasta";
+ } else {
+ if (wgsProtocol.toUpperCase().equals("IONTORRENT")) {
+ newName = baseName+"."+Integer.toString(fileCount)+".fastq.gz";
+ } else {
+ newName = baseName+".R"+Integer.toString(fileCount)+".fastq.gz";
+ }
+ }
+
+ // Calculate checksum for file, to put in XML
+ FileInputStream fis;
+ String checksum;
+ try {
+ fis = new FileInputStream(f);
+ checksum = org.apache.commons.codec.digest.DigestUtils.md5Hex(fis);
+ fis.close();
+ } catch (IOException e) {
+ log("Checksum failed");
+ fail++;
+ continue ISOLATE;
+ }
+ newFileNames.put(f,newName);
+ fileNamesXml.add(newName);
+ checksumsXml.add(checksum);
+ fileCount++;
+ }
+
+
+ boolean xmlSuccess = makeSftpXml(xmlFile,baseName,fileNamesXml,wgsProtocol,checksumsXml,ui,cfg.isAnonymizeFtp(),cfg.isShareFtp(),cfg.isShowYearFtp());
+ if (!xmlSuccess) {
+ log("XML creation failed.");
+ fail++;
+ continue ISOLATE;
+ }
+
+ // Upload XML file
+ try {
+ String uriXml = "sftp://"+sftpLogin+":"+sftpPass+"@"+sftpHost+":22"+remotePath+"/"+xmlName;
+ FileObject localFileXml;
+ localFileXml = fsManager.resolveFile(xmlFile);
+ FileObject foXml = fsManager.resolveFile(uriXml, fsOptions);
+ foXml.copyFrom(localFileXml, Selectors.SELECT_SELF);
+ } catch (FileSystemException e1) {
+ log("XML upload failed.");
+ fail++;
+ continue ISOLATE;
+ }
+
+
+ // Upload files
+ for (File f : files) {
+
+ // Check if process is cancelled
+ if (isStopped()) {
+ log("Process cancelled");
+ return null;
+ }
+ setStatus("File "+f.getName());
+ setProgress(10+i*step+count*step2);
+ log("File "+f.getName());
+
+ boolean done = false;
+ int tries = 0;
+
+ // Upload will retry several times, as determined by the MAX_TRIES parameter
+ while (!done) {
+ done=true;
+ count++;
+
+ String newName = newFileNames.get(f);
+
+ // Check local file size, to determine if upload is successful
+ long filesizeLocal = f.length();
+
+ try {
+
+ // Remote file and folder objects
+ String uri = "sftp://"+sftpLogin+":"+sftpPass+"@"+sftpHost+":22"+remotePath+"/"+newName;
+ String uriDir = "sftp://"+sftpLogin+":"+sftpPass+"@"+sftpHost+":22"+remotePath;
+
+ FileObject fileObjectLocal = fsManager.resolveFile(f.getAbsolutePath());
+ FileObject folderObjectRemote = fsManager.resolveFile(uriDir, fsOptions);
+ folderObjectRemote.createFolder();
+ FileObject fileObjectRemote = fsManager.resolveFile(uri, fsOptions);
+
+ log("Uploading to SFTP: "+fileObjectLocal.toString()+"\n");
+ log("Remote file: "+fileObjectRemote.getPublicURIString()+"\n");
+
+ fileObjectRemote.copyFrom(fileObjectLocal, Selectors.SELECT_SELF);
+
+ long filesizeUploaded = fileObjectRemote.getContent().getSize();
+
+ // Check if the file is successfully uploaded
+ if (filesizeUploaded != filesizeLocal) {
+ log("Upload error (wrong file size) for "+fileObjectLocal.toString());
+ done = false;
+ }
+
+ if (done) {
+ log("Upload for "+fileObjectLocal.toString()+" finished");
+ }
+
+ } catch (Exception e) {
+
+ // If Exception, log it and retry
+ e.printStackTrace();
+ log(e.getStackTrace().toString());
+ done = false;
+ }
+
+ if (!done) {
+ tries++;
+ // Check if the maximum number of tries have been reached
+ if (tries fileNames, String wgsProtocol, ArrayList checksums, String ui, boolean anonymize, boolean share, boolean showYear) {
+
+ try {
+
+ Element ecdcWgs = new Element("ECDCWgs");
+ Element sequence = new Element("sequence");
+
+ Element ecdcWgsUploadClient = new Element("ecdcWgsUploadClient");
+
+ // Add XML API version to XML, must match back-end version
+ // TODO: Incorrect version should trigger user notification from back-end using email
+ ecdcWgsUploadClient.setAttribute(new Attribute("version", XML_API_VERSION));
+ Document doc = new Document(ecdcWgs);
+ ecdcWgs.addContent(sequence);
+ sequence.addContent(ecdcWgsUploadClient);
+
+ // Add current date and time
+ SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+ Element uploadedDate = new Element("submittedDate");
+ uploadedDate.setText(df.format(new Date()));
+ ecdcWgsUploadClient.addContent(uploadedDate);
+
+ // Add the anonymized id
+ Element anonymizedId = new Element("anonymizedId");
+ anonymizedId.setText(baseName);
+ ecdcWgsUploadClient.addContent(anonymizedId);
+
+ // Add info for each sequence file
+ for (int i = 0; i< fileNames.size();i++) {
+ Element sequenceReads = new Element("sequenceReads");
+ sequenceReads.addContent(new Element("fileName").setText(fileNames.get(i)));
+ sequenceReads.addContent(new Element("wgsProtocol").setText(wgsProtocol));
+ sequenceReads.addContent(new Element("md5Checksum").setText(checksums.get(i)));
+ ecdcWgsUploadClient.addContent(sequenceReads);
+ }
+
+ // Add parameters for the back-ens processing
+ Element config = new Element("ecdcWgsConfig");
+ ecdcWgsUploadClient.addContent(config);
+
+ // Is this associated with an event id?
+ config.addContent(new Element("event").setText(ui));
+
+ // Should the file names be anonymized if shared
+ config.addContent(new Element("anonymise").setText(Boolean.toString(anonymize)));
+
+ // Should the files be shared?
+ config.addContent(new Element("share").setText(Boolean.toString(share)));
+
+ // Should the year be shown if shared?
+ config.addContent(new Element("showYear").setText(Boolean.toString(showYear)));
+
+ // Write XML
+ XMLOutputter xmlOutput = new XMLOutputter();
+ xmlOutput.setFormat(Format.getPrettyFormat());
+ xmlOutput.output(doc, new FileWriter(xmlFile));
+ log("xmlFile written: "+xmlFile);
+
+ } catch (IOException io) {
+ log("Error, could not write xmlFile: "+xmlFile);
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/src/eu/europa/ecdc/enauploader/EcdcTessyApprovalWorker.java b/src/eu/europa/ecdc/enauploader/EcdcTessyApprovalWorker.java
new file mode 100644
index 0000000..f6a76fa
--- /dev/null
+++ b/src/eu/europa/ecdc/enauploader/EcdcTessyApprovalWorker.java
@@ -0,0 +1,95 @@
+package eu.europa.ecdc.enauploader;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.HashMap;
+
+// Class for submitting reads and assembly to SFTP
+public class EcdcTessyApprovalWorker extends EcdcJob {
+
+ // Mandatory constructor
+ EcdcTessyApprovalWorker(EcdcUploaderGUI gui, TessyBatch batch, String name, UploadConfig cfg, String[] headers,
+ String[][] data) {
+ super(gui, batch, name, cfg, headers, data);
+
+ }
+
+ // Main process for the event
+ @Override
+ protected Object doInBackground() throws Exception {
+
+ String batchId = batch.getBatchId();
+
+ // Check if process was interrupted
+ if (isStopped()) {
+ log("Process cancelled");
+ return null;
+ }
+ log("Approving batch "+batchId);
+
+ setTitle("Approving batch "+batchId);
+ setStatus("Retrieving validation...");
+ setProgress(5);
+
+ // Batch must have passed validation before approval
+ if (!batch.passedValidation()) {
+ log("Batch did not pass validation, it cannot be approved. See deails below. Reject the batch and restart.");
+
+ HashMap results = batch.getValidationResults();
+ for (String k : results.keySet()) {
+ TessyValidationResult val = results.get(k);
+ log(val.toString());
+ }
+
+ setStatus("Error, batch did not pass validation");
+ setProgress(10);
+ return null;
+ }
+
+ // Check if process is interrupted
+ if (isStopped()) {
+ log("Process cancelled");
+ return null;
+ }
+
+ setStatus("Approving...");
+ setProgress(30);
+
+ // Send approve request to TESSy
+ log("Sending approve request");
+ boolean approved = batch.approve();
+
+ // Get TESSy reply
+ String msg = batch.getLastMessage();
+ log("Uploaded XML: "+msg);
+ log("TESSy response: "+batch.getLastResponse());
+
+ // If approval failed, exit with error
+ if (!approved) {
+ setStatus("Error, approval failed.");
+ log("Error, approval failed");
+ setProgress(35);
+ gui.error("TESSy approval of batch "+batchId+" failed");
+ return null;
+ }
+
+ // Update GUI with TESSy approval date and save
+ log("Saving...");
+ setStatus("Saving...");
+ setProgress(95);
+
+ SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+ String dateStr = df.format(new Date());
+ log("Updating GUI with active batch");
+ gui.setBatch(name, batch);
+ log("Updating Isolate table");
+ gui.setValidationResults(name, batch.getBatchId(),"A",null,dateStr,true);
+ log("Saving");
+ gui.save();
+
+ // Exit
+ setStatus("Finished!");
+ setProgress(100);
+ return null;
+ }
+}
diff --git a/src/eu/europa/ecdc/enauploader/EcdcTessyCreateAndTestWorker.java b/src/eu/europa/ecdc/enauploader/EcdcTessyCreateAndTestWorker.java
new file mode 100644
index 0000000..34666a0
--- /dev/null
+++ b/src/eu/europa/ecdc/enauploader/EcdcTessyCreateAndTestWorker.java
@@ -0,0 +1,208 @@
+package eu.europa.ecdc.enauploader;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+import javax.xml.bind.DatatypeConverter;
+
+// Class for creating a batch object and testing it in TESSy
+public class EcdcTessyCreateAndTestWorker extends EcdcJob {
+
+ // Mandatory constructor
+ EcdcTessyCreateAndTestWorker(EcdcUploaderGUI gui, TessyBatch batch, String name, UploadConfig cfg, String[] headers,
+ String[][] data) {
+ super(gui, batch, name, cfg, headers, data);
+ }
+
+ // Main routine for job
+ @Override
+ protected Object doInBackground() {
+
+ // Check if job is interrupted
+ if (isStopped()) {
+ log("Process cancelled");
+ return null;
+ }
+ log("Started");
+ setStatus("Requesting new batchId...");
+ setProgress(5);
+
+ // Create a TESSy request for checking last batchId
+ // A little bit of a hack to use the TessyIsolate class like this, but it works nicely.
+ TessyIsolate iso = new TessyIsolate("","","");
+ iso.setCredentials(batch.getCredentials());
+ ArrayList content = new ArrayList();
+ content.add(" ");
+ log("XML submitted: "+content);
+ String response = iso.submitXml(content);
+ log("TESSy response: "+response);
+ if (response==null) {
+ setStatus("Error, failed to get next batchId");
+ setProgress(10);
+ gui.error("Retrieving last used batch id from TESSy failed.");
+ log("No batchId returned.");
+ return null;
+ }
+
+ // Check if job is interrupted
+ if (isStopped()) {
+ log("Process cancelled");
+ return null;
+ }
+
+ setStatus("Parsing batchId...");
+ setProgress(30);
+
+ // Parse the batchId
+ // TODO: The entire batchId retrieval could be moved to a method and improved
+ String[] lines = response.split("\n",-1);
+ boolean foundId = false;
+ for (String l : lines) {
+ if (l.matches(".*.*")) {
+ String batchStr = l.replaceAll(".*","").replaceAll(" .*","");
+ log("Last used batchId: "+batchStr);
+ int batchnum = Integer.parseInt(batchStr);
+ batchnum++;
+ batchStr = Integer.toString(batchnum);
+ log("Next batchId: "+batchStr);
+ batch.setId(batchStr);
+ foundId = true;
+ }
+ }
+
+ // If fail to get next batchId from TESSy, exit
+ if (!foundId) {
+ setStatus("Error, failed to get next batchId");
+ setProgress(35);
+ gui.error("Retrieving last used batch id from TESSy failed.");
+ log("No batchId returned.");
+ return null;
+ }
+
+ // Check if interrupted
+ if (isStopped()) {
+ log("Process cancelled");
+ return null;
+ }
+
+ setStatus("Creating batch...");
+ log("Creating batch...");
+ setTitle("Testing batch "+batch.getBatchId());
+ setProgress(35);
+
+ // Put data table in arraylist
+ // TODO: Data table API between GUI and jobs should be improved
+ ArrayList rows = new ArrayList();
+ for (int i = 0;i results = batch.test();
+
+ // If results are somehow null, give an error
+ if (results == null) {
+ setStatus("Error, failed to find validation results");
+ setProgress(50);
+ log("Error, failed to find validation results (null response from batch.test()).");
+ return null;
+ }
+
+ // Log the validation results
+ for (String k : results.keySet()) {
+ TessyValidationResult val = results.get(k);
+ log(val.toString());
+ }
+
+
+ setStatus("Saving...");
+ setProgress(90);
+ log("Setting current batch: "+name+": "+batch.getBatchId()+" ("+batch.getState()+")");
+
+ // Update the GUI with batch information
+ log("Updating isolate table with batch information");
+ gui.setBatch(name, batch);
+
+ // Update the GUI with validation results
+ log("Updating isolate table with validation results");
+ gui.setValidationResults(name, batch.getBatchId(),"T",null,null,false);
+
+ // Save
+ log("Saving results...");
+ gui.save();
+
+
+ // Exit
+ log("Finished!");
+ setStatus("Finished!");
+ setProgress(100);
+
+ return null;
+ }
+}
diff --git a/src/eu/europa/ecdc/enauploader/EcdcTessyRejectWorker.java b/src/eu/europa/ecdc/enauploader/EcdcTessyRejectWorker.java
new file mode 100644
index 0000000..97e45b5
--- /dev/null
+++ b/src/eu/europa/ecdc/enauploader/EcdcTessyRejectWorker.java
@@ -0,0 +1,54 @@
+package eu.europa.ecdc.enauploader;
+
+// Class for sending reject requests to TESSy
+public class EcdcTessyRejectWorker extends EcdcJob {
+
+ // Mandatory constructor
+ EcdcTessyRejectWorker(EcdcUploaderGUI gui, TessyBatch batch, String name, UploadConfig cfg, String[] headers,
+ String[][] data) {
+ super(gui, batch, name, cfg, headers, data);
+ }
+
+ // Main routine for the job
+ @Override
+ protected Object doInBackground() throws Exception {
+ String batchId = batch.getBatchId();
+
+ // Check if the process is interrupted
+ if (isStopped()) {
+ log("Process cancelled");
+ return null;
+ }
+ log("Rejecting batch "+batchId);
+ setTitle("Rejecting batch "+batchId);
+ setProgress(15);
+
+ // Send reject request to TESSy
+ batch.reject();
+
+ // Get TESSy request and reply texts
+ String msg = batch.getLastMessage();
+ log("Uploaded XML: "+msg);
+ log("TESSy response: "+batch.getLastResponse());
+
+ // Update GUI and save
+ log("Saving...");
+ setStatus("Saving...");
+ setProgress(95);
+
+ log("Updating Isolate table");
+ gui.setValidationResults(name, batch.getBatchId(),"R",null,null,true);
+ log("Updating GUI with active batch");
+ gui.setBatch(name, batch);
+
+ log("Saving");
+ gui.save();
+
+ log("Finished.");
+ setStatus("Finished!");
+ setProgress(100);
+
+ return null;
+ }
+
+}
diff --git a/src/eu/europa/ecdc/enauploader/EcdcTessyUploadWorker.java b/src/eu/europa/ecdc/enauploader/EcdcTessyUploadWorker.java
new file mode 100644
index 0000000..587ea91
--- /dev/null
+++ b/src/eu/europa/ecdc/enauploader/EcdcTessyUploadWorker.java
@@ -0,0 +1,154 @@
+package eu.europa.ecdc.enauploader;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.HashMap;
+
+// Class for uploading a created and tested TESSy batch
+public class EcdcTessyUploadWorker extends EcdcJob {
+
+
+ // Mandatory constructor
+ EcdcTessyUploadWorker(EcdcUploaderGUI gui, TessyBatch batch, String name, UploadConfig cfg, String[] headers,
+ String[][] data) {
+ super(gui, batch, name, cfg, headers, data);
+
+ }
+
+ // Constants
+ private static final long WAITTIME = 5000; // Time to wait between tries, in ms
+ private static final int MAXTRIES = 100; // How many times to wait for validation before giving up
+ // Multiply these together to get the maximum total time before validation timeout
+
+ // Main process for the job
+ @Override
+ protected Object doInBackground() {
+
+ //Check if interrupted
+ if (isStopped()) {
+ log("Process cancelled");
+ return null;
+ }
+
+ log("Started");
+ String batchId = batch.getBatchId();
+ log("batchId "+batchId);
+
+ setTitle("Uploading batch "+batchId);
+ setStatus("Uploading batch...");
+ setProgress(5);
+
+ // Check that batch has passed the test
+ if (!batch.passedValidation()) {
+ log("Batch did not pass test, it cannot be uploaded.");
+ setStatus("Error, batch did not pass test");
+ setProgress(10);
+ return null;
+ }
+
+ log("Uploading");
+
+ //Upload batch
+ boolean uploaded = batch.upload();
+
+ log("TESSy response: "+batch.getLastResponse());
+
+ // Check if upload was successful
+ if (!uploaded) {
+ setStatus("Error, upload failed.");
+ log("Error, upload failed.");
+ setProgress(10);
+ gui.error("TESSy upload of batch "+batchId+" failed");
+ return null;
+ }
+
+ // Update UI with information that the batch has been uploaded
+ SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+ String dateStr = df.format(new Date());
+ log("Batch uploaded at "+ dateStr);
+ gui.setBatch(name, batch);
+ gui.setValidationResults(name, batch.getBatchId(),"U",dateStr,null,true);
+
+ setStatus("Waiting for validation results...");
+ log("Waiting for validation...");
+ setProgress(30);
+
+ // Wait for validation results
+ boolean done = false;
+ int tries = 0;
+ while (!done) {
+
+ // If giving up, show error in GUI
+ if (tries > MAXTRIES) {
+ setStatus("Error, validation results timeout after "+Long.toString(MAXTRIES*WAITTIME/1000)+" seconds");
+ setProgress(35);
+ gui.error("TESSy validation of batch "+batchId+" timed out after "+Long.toString(MAXTRIES*WAITTIME/1000)+" seconds.");
+ return null;
+ }
+
+ // Check if validation is ready
+ tries++;
+ log("Validation check #"+Integer.toString(tries));
+ done = batch.checkValidation();
+ log("Results available: "+Boolean.toString(done));
+
+ // Just some funky code to move the progressbar back and forth while waiting
+ if (tries%2==0) {
+ setProgress(40);
+ } else {
+ setProgress(35);
+ }
+
+ // Wait before next attempt to retrieve validations
+ try {
+ Thread.sleep(WAITTIME);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+
+ log("Retrieving validation results.");
+ setStatus("Retrieving validation results");
+ setProgress(65);
+
+ // Retrieve validation results
+ HashMap results = batch.getValidation();
+ String msg = batch.getLastMessage();
+ log("Uploaded XML: "+msg);
+
+ // Check if results are present, else show an error
+ if (results==null) {
+ log("Error, null results retrieved.");
+ setStatus("Error while retrieving validation results");
+ setProgress(65);
+ gui.error("ERROR while retrieving validation results. You must retrieve validation results before proceeding.");
+ return null;
+ }
+
+ // Log the validation results
+ for (String k : results.keySet()) {
+ TessyValidationResult val = results.get(k);
+ log(val.toString());
+ }
+
+ setStatus("Saving...");
+ setProgress(95);
+
+ // Update GUI with batch info and validation results
+ log("Updating active batch in GUI");
+ gui.setBatch(name, batch);
+ log("Updating isolate table");
+ gui.setValidationResults(name, batch.getBatchId(),"V",dateStr,null,true);
+
+ // Save GUI
+ log("Saving...");
+ gui.save();
+
+ // Exit
+ setStatus("Finished!");
+ setProgress(100);
+ log("Finished.");
+ return null;
+ }
+
+}
diff --git a/src/eu/europa/ecdc/enauploader/EcdcUploaderGUI.java b/src/eu/europa/ecdc/enauploader/EcdcUploaderGUI.java
new file mode 100644
index 0000000..f532b04
--- /dev/null
+++ b/src/eu/europa/ecdc/enauploader/EcdcUploaderGUI.java
@@ -0,0 +1,1909 @@
+package eu.europa.ecdc.enauploader;
+
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.ComponentAdapter;
+import java.awt.event.ComponentEvent;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.awt.image.BufferedImage;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+
+import javax.imageio.ImageIO;
+import javax.swing.DefaultCellEditor;
+import javax.swing.JComboBox;
+import javax.swing.JFileChooser;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JMenu;
+import javax.swing.JMenuBar;
+import javax.swing.JMenuItem;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTabbedPane;
+import javax.swing.JTable;
+import javax.swing.KeyStroke;
+import javax.swing.ListSelectionModel;
+import javax.swing.RowSorter;
+import javax.swing.SortOrder;
+import javax.swing.SwingUtilities;
+import javax.swing.UIManager;
+import javax.swing.UnsupportedLookAndFeelException;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+import javax.swing.event.TableModelEvent;
+import javax.swing.event.TableModelListener;
+import javax.swing.filechooser.FileFilter;
+import javax.swing.filechooser.FileNameExtensionFilter;
+import javax.swing.table.DefaultTableCellRenderer;
+import javax.swing.table.DefaultTableModel;
+import javax.swing.table.TableColumn;
+import javax.swing.table.TableRowSorter;
+
+public class EcdcUploaderGUI extends JFrame implements ActionListener {
+
+ private static final long serialVersionUID = 1268136970945881244L;
+
+ // Column names for reserved columns in the isolate table
+ private static final String BASE_FILE_COLUMN = "File base name";
+ private static final String ASSEMBLY_FILE_COLUMN = "Assembly file";
+ private static final String READ_FILES_COLUMN = "Read files";
+ private static final String MODIFIED_COLUMN = "Modified date";
+ private static final String UPLOAD_COLUMN = "Ready for upload";
+ private static final String UI_COLUMN = "ECDC event (UI)";
+ private static final String TESSY_ID_COLUMN = "TESSy id";
+ private static final String TESSY_BATCH_COLUMN = "TESSy batch";
+ private static final String TESSY_TEST_COLUMN = "Selected for TESSy upload";
+ private static final String TESSY_UPLOADED_COLUMN = "TESSy last uploaded";
+ private static final String ENA_RUN_COLUMN = "ENA run id";
+ private static final String TESSY_VALIDATION_COLUMN = "TESSy validation";
+ private static final String TESSY_APPROVED_COLUMN = "TESSy last approved";
+ private static final String SFTP_COLUMN = "ECDC SFTP uploaded";
+
+ private static final String VERSION = "1.0.3";
+
+
+
+
+ private SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+
+ // Config directory = same as working directory,
+ private static final String CONFIG_DIR = ".";
+
+ protected static final int EDITABLE_EXTRA_FIELDS = 5;
+ private static final int LOCKED_FIELDS = 8;
+
+ // Is this class run using the main method?
+ private boolean isMain;
+
+ // HashMap of isolate tables
+ private HashMap tableHash;
+
+ // HashMap of upload configurations for each table and any number of import configurations
+ private HashMap configHash;
+ private HashMap importConfigHash;
+
+ // HashMap of TESSy batches in progress and a HashMap with the TESSy SUBJECTs for those batches.
+ // TODO: Improve the handling of batches, old batches remain sometimes when the program is closed during upload.
+ private HashMap batches;
+ private HashMap batchSubjects;
+
+ // Fields in addition to the metadata fields (reserved fields)
+ private ArrayList extraFields;
+
+ // UI elements that need to be accessed as fields
+ private JTabbedPane tabs;
+ private JMenu importQuickMenu;
+ private JScrollPane scroller;
+ private JLabel totalEntriesLabel;
+ private JLabel selectedEntriesLabel;
+
+ // Import scheduler for automated submissions, and job handler for submission jobs
+ private ImportScheduler scheduler;
+ private EcdcJobHandler jobHandler;
+
+ private ImportConfigGUI importDialogWindow;
+
+ private UploadConfigGUI uploadDialogWindow;
+
+
+
+
+ public EcdcUploaderGUI(boolean isMain) {
+ this.isMain = isMain;
+ }
+
+ public static void main(String[] args) {
+
+ // Set System L&F
+ try {
+ UIManager.setLookAndFeel(
+ UIManager.getSystemLookAndFeelClassName());
+ } catch (ClassNotFoundException | InstantiationException | IllegalAccessException
+ | UnsupportedLookAndFeelException e) {
+ e.printStackTrace();
+ }
+
+ // Create UI, with the isMain flag set to true
+ EcdcUploaderGUI gui = new EcdcUploaderGUI(true);
+
+ // Init UI
+ gui.init();
+
+ }
+
+ private void init() {
+
+ // Initialize variables
+ batches = new HashMap();
+ batchSubjects = new HashMap();
+
+ importConfigHash = new HashMap();
+
+
+
+ // Add all the global reserved fields to the extraFields list
+ extraFields = new ArrayList();
+ extraFields.add(MODIFIED_COLUMN);
+ extraFields.add(UPLOAD_COLUMN);
+ extraFields.add(UI_COLUMN);
+ extraFields.add(BASE_FILE_COLUMN);
+ extraFields.add(READ_FILES_COLUMN);
+ extraFields.add(ASSEMBLY_FILE_COLUMN);
+ extraFields.add(TESSY_TEST_COLUMN );
+ extraFields.add(TESSY_BATCH_COLUMN );
+ extraFields.add(TESSY_VALIDATION_COLUMN);
+ extraFields.add(TESSY_UPLOADED_COLUMN);
+ extraFields.add(TESSY_APPROVED_COLUMN);
+ extraFields.add(TESSY_ID_COLUMN);
+ extraFields.add(ENA_RUN_COLUMN);
+ extraFields.add(SFTP_COLUMN);
+
+
+ // Init UI elements
+ totalEntriesLabel = new JLabel("");
+ selectedEntriesLabel = new JLabel("");
+ JPanel mainPanel = new JPanel(new BorderLayout());
+ JMenuBar menuBar = new JMenuBar();
+ JMenu fileMenu = new JMenu("Data");
+ JMenu configMenu = new JMenu("Setup");
+ JMenu uploadMenu = new JMenu("Submission");
+ JMenu viewMenu = new JMenu("View");
+ menuBar.add(fileMenu);
+ menuBar.add(configMenu);
+ menuBar.add(uploadMenu);
+ menuBar.add(viewMenu);
+
+ JMenuItem addrowItem = new JMenuItem("Add row");
+ addrowItem.addActionListener(this);
+ addrowItem.setActionCommand("addrow");
+ KeyStroke ctrlAKeyStroke = KeyStroke.getKeyStroke("control ENTER");
+ addrowItem.setAccelerator(ctrlAKeyStroke);
+ fileMenu.add(addrowItem);
+
+ JMenuItem addrowsItem = new JMenuItem("Add several rows");
+ addrowsItem.addActionListener(this);
+ addrowsItem.setActionCommand("addrows");
+ KeyStroke ctrlAAKeyStroke = KeyStroke.getKeyStroke("control alt ENTER");
+ addrowsItem.setAccelerator(ctrlAAKeyStroke);
+ fileMenu.add(addrowsItem);
+
+ JMenuItem removerowsItem = new JMenuItem("Delete selected rows");
+ removerowsItem.addActionListener(this);
+ removerowsItem.setActionCommand("removerows");
+ fileMenu.add(removerowsItem);
+
+ JMenuItem saveItem = new JMenuItem("Save data");
+ saveItem.addActionListener(this);
+ saveItem.setActionCommand("save");
+ KeyStroke ctrlSKeyStroke = KeyStroke.getKeyStroke("control S");
+ saveItem.setAccelerator(ctrlSKeyStroke);
+ fileMenu.add(saveItem);
+
+ JMenuItem loadItem = new JMenuItem("Revert to saved");
+ loadItem.addActionListener(this);
+ loadItem.setActionCommand("revert");
+ KeyStroke ctrlLKeyStroke = KeyStroke.getKeyStroke("control L");
+ loadItem.setAccelerator(ctrlLKeyStroke);
+ fileMenu.add(loadItem);
+
+ fileMenu.addSeparator();
+
+ importQuickMenu = new JMenu("Import using template");
+ fileMenu.add(importQuickMenu);
+
+ loadImportAndScheduleConfigs(importQuickMenu);
+
+ JMenuItem importMenuItem = new JMenuItem("Manage imports and scheduling...");
+ importMenuItem.addActionListener(this);
+ importMenuItem.setActionCommand("importDialog");
+ configMenu.add(importMenuItem);
+
+ JMenuItem configItem = new JMenuItem("Configure upload parameters...");
+ configItem.addActionListener(this);
+ configItem.setActionCommand("config");
+ configMenu.add(configItem);
+
+ JMenuItem linkItem = new JMenuItem("Link sequence files for selected isolates");
+ linkItem.addActionListener(this);
+ linkItem.setActionCommand("link");
+ uploadMenu.add(linkItem);
+
+ uploadMenu.addSeparator();
+
+ JMenuItem allUploadItem = new JMenuItem("Submit data to configured systems");
+ allUploadItem.addActionListener(this);
+ allUploadItem.setActionCommand("uploadSelected");
+ uploadMenu.add(allUploadItem);
+
+ uploadMenu.addSeparator();
+
+ JMenuItem enaUploadSelectedItem = new JMenuItem("ENA - Submit all selected isolates");
+ enaUploadSelectedItem.addActionListener(this);
+ enaUploadSelectedItem.setActionCommand("uploadEnaSelected");
+ uploadMenu.add(enaUploadSelectedItem);
+
+ uploadMenu.addSeparator();
+
+ JMenuItem testSelectedItem = new JMenuItem("TESSy - Create batch from selected and test");
+ testSelectedItem.addActionListener(this);
+ testSelectedItem.setActionCommand("testSelected");
+ uploadMenu.add(testSelectedItem);
+
+ JMenuItem uploadAllItem = new JMenuItem("TESSy - Upload batch...");
+ uploadAllItem.addActionListener(this);
+ uploadAllItem.setActionCommand("uploadBatch");
+ uploadMenu.add(uploadAllItem);
+
+ JMenuItem approveAllItem = new JMenuItem("TESSy - Approve batch...");
+ approveAllItem.addActionListener(this);
+ approveAllItem.setActionCommand("approveBatch");
+ uploadMenu.add(approveAllItem);
+
+ JMenuItem rejectItem = new JMenuItem("TESSy - Reject batch...");
+ rejectItem.addActionListener(this);
+ rejectItem.setActionCommand("rejectBatch");
+ uploadMenu.add(rejectItem);
+
+ uploadMenu.addSeparator();
+ uploadMenu.addSeparator();
+
+ JMenuItem sftpUploadItem = new JMenuItem("SFTP - Upload sequences (requires TESSy records)");
+ sftpUploadItem.addActionListener(this);
+ sftpUploadItem.setActionCommand("uploadFtpSelected");
+ uploadMenu.add(sftpUploadItem);
+
+ JMenuItem viewJobsItem = new JMenuItem("Show job manager window");
+ viewJobsItem.addActionListener(this);
+ viewJobsItem.setActionCommand("viewJobs");
+ viewMenu.add(viewJobsItem);
+
+ this.setJMenuBar(menuBar);
+
+ // Set program Icon and title
+ setTitle("ECDC WGS upload app v"+VERSION);
+ try {
+ String imagePath = "media/ECDC2.png";
+ InputStream imgStream = UploadConfigGUI.class.getResourceAsStream(imagePath );
+ BufferedImage myImg;
+ myImg = ImageIO.read(imgStream);
+ setIconImage(myImg);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+
+ // load isolate table contents from files
+ loadData();
+
+ // load configs from files
+ loadUploadConfigs();
+
+ JPanel titlePanel = new JPanel();
+ JLabel titleLabel = new JLabel("Isolate table");
+
+
+
+ updateEntriesLabels();
+
+
+ titlePanel.add(titleLabel);
+ JLabel totalText = new JLabel(" Total entries: ");
+ titlePanel.add(totalText);
+ titlePanel.add(totalEntriesLabel);
+ JLabel selectedText = new JLabel(" Selected: ");
+ titlePanel.add(selectedText);
+ titlePanel.add(selectedEntriesLabel);
+ mainPanel.add(titlePanel,BorderLayout.NORTH);
+
+ mainPanel.add(tabs,BorderLayout.CENTER);
+ this.add(mainPanel);
+
+ // If this is the main class, close program on window close.
+ // (This class could be instantiated from elsewhere, then it should not exit on close)
+ if (isMain) {
+ this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+ }
+
+
+
+ // Init the job handler
+ jobHandler = new EcdcJobHandler();
+
+ // Init the automated job scheduler
+ scheduler = new ImportScheduler(this);
+ scheduler.execute();
+
+ // Send the config information to the scheduler which can import and upload at regular intervals if so configured
+ int scheduleNumber = scheduler.setImportConfigHash(importConfigHash);
+
+ // Notify the user of scheduled submissions.
+ if (scheduleNumber>0) {
+ SwingUtilities.invokeLater(new Runnable() {
+ @Override
+ public void run() {
+ JOptionPane.showMessageDialog(null,
+ "Automatic import and submission has been configured for "+Integer.toString(scheduleNumber)+" different import configuration(s). This will automatically submit data to configured systems. Go to the 'Manage imports and scheduling...' wizard if you wish to change this. Note that scheduled import and submission only works if you keep the ECDC WGS Upload app open.");
+ }
+
+ });
+
+ }
+
+ // Set UI size and make it visible
+ this.setSize(1024,800);
+ this.setExtendedState(JFrame.MAXIMIZED_BOTH);
+ this.setVisible(true);
+ }
+
+ private void updateEntriesLabels() {
+ String name= tabs.getTitleAt(tabs.getSelectedIndex());
+ JTable table = tableHash.get(name);
+ totalEntriesLabel.setText(Integer.toString(table.getRowCount()));
+ selectedEntriesLabel.setText(Integer.toString(table.getSelectedRowCount()));
+
+ }
+
+ private void loadUploadConfigs() {
+
+ // Init empty HashMap for configs
+ configHash = new HashMap();
+
+ // Iterate through all the tabs and look for a config associated with the SUBJECT
+ int totalTabs = tabs.getTabCount();
+ for(int i = 0; i < totalTabs; i++)
+ {
+ String name= tabs.getTitleAt(i);
+ File configFile = new File(name+".cfg");
+
+ // If config does not exist, it must be created before any upload can be performed, warn the user
+ if (!configFile.exists()) {
+ JOptionPane.showMessageDialog(null,
+ "No config found for "+name+", you must create a config called \'"+name+".cfg\' before you can submit any data for "+name+".\nConfigs are created from the \'Setup\' menu");
+ continue;
+ }
+
+ //Load config from file and add it to HashMap
+ try {
+ UploadConfig cfg = UploadConfigHandler.loadConfig(configFile);
+ configHash.put(name, cfg);
+ } catch (Exception e) {
+ e.printStackTrace();
+ JOptionPane.showMessageDialog(this,
+ "Loading config "+name+".cfg failed.");
+ continue;
+ }
+
+
+ }
+ }
+
+
+ private void loadData() {
+
+ // Init the actual TabbedPane and a HashMap for associating SUBJECT and tab
+ tabs = new JTabbedPane();
+ tableHash = new HashMap();
+
+ tabs.addChangeListener(new ChangeListener() {
+ @Override
+ public void stateChanged(ChangeEvent arg0) {
+ updateEntriesLabels();
+
+ }
+ });
+
+
+ // List all files in config directory and iterate through them
+ File dir = new File(CONFIG_DIR);
+ File[] files = dir.listFiles();
+ for (File f : files) {
+
+ // If a batch file is found, load batches from it
+ if (f.getName().endsWith(".batch")) {
+ String name = f.getName().substring(0,f.getName().length()-6);
+ loadBatchFiles(name);
+ }
+
+ // If this is a metadata file (supplied by ECDC), create a tab including an isolate table
+ if (f.getName().endsWith(".meta")) {
+ String[] headers;
+ String[][] data;
+
+ // Name of tab derived from metadata file name
+ String name = f.getName().substring(0,f.getName().length()-5);
+
+ // Initialize controlled vocabularies use for some fields (coded value lists)
+ HashMap controlledVocabularies = new HashMap();
+
+ BufferedReader br;
+ ArrayList fields = new ArrayList();
+ try {
+ // Open file
+ br = new BufferedReader(new FileReader(f));
+ String line;
+
+ // Each row contains a fieldname, and potentially a comma-separated coded value list after =
+ while ((line=br.readLine())!=null) {
+ fields.add(line);
+ }
+ // Add the global reserved fields to the list of fields
+ for (String s : extraFields) {
+ if (fields.indexOf(s)==-1) {
+ fields.add(s);
+ }
+ }
+
+ // Create a table header with size equal to the number of fields
+ headers = new String[fields.size()];
+
+ // For every field that contains '=', split it and use the comma-separated values after the = as a controlled vocabulary
+ for (int i=0;ithis.getColumnCount()-extraFields.size()+EDITABLE_EXTRA_FIELDS) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+ };
+
+ // Create the isolate table
+ JTable table = new JTable(model){
+
+ private static final long serialVersionUID = -7197366759070120859L;
+
+
+ // Set the tooltip text so that mouse-over shows the contents. For content containing ';', split over several rows
+ public String getToolTipText(MouseEvent e) {
+ String tip = "";
+ java.awt.Point p = e.getPoint();
+ int rowIndex = rowAtPoint(p);
+ int colIndex = columnAtPoint(p);
+
+
+ try {
+ String[] tips = getValueAt(rowIndex, colIndex).toString().split(";");
+ for (String t : tips) {
+ tip = tip+t+" ";
+ }
+ } catch (RuntimeException e1) {
+ // Do nothing on error (it is hard to envision what could go wrong here anyway)
+ }
+ if (tip.equals("")) {
+ return null;
+ }
+ tip = ""+tip+"";
+ return tip;
+ }
+ };
+
+
+
+
+ // When new rows are added, scroll to bottom of table. Must be done using invokeLater, as the table is not updated visually
+ // on the same thread as the TableModel modification
+ table.getModel().addTableModelListener(new TableModelListener() {
+ @Override
+ public void tableChanged(TableModelEvent e) {
+ if (e.getType()==TableModelEvent.DELETE) {
+ System.out.println("DELETE");
+ return;
+ }
+ SwingUtilities.invokeLater(new Runnable() {
+ @Override
+ public void run() {
+ if (e.getType()==TableModelEvent.INSERT) {
+ table.scrollRectToVisible(table.getCellRect(table.getRowCount()-1, 0, true));
+ updateEntriesLabels();
+ }
+ int col = e.getColumn();
+ int dateCol=getColumn(table,MODIFIED_COLUMN);
+ if (col=table.getColumnCount()-extraFields.size()+1+EDITABLE_EXTRA_FIELDS) {
+
+ if (isSelected) {
+ c.setBackground(new Color(155, 155, 155));
+ } else {
+
+ c.setBackground(new Color(175, 235, 235));
+
+ }
+ } else if (column>=table.getColumnCount()-extraFields.size()+3) {
+ if (isSelected) {
+ c.setBackground(new Color(155, 155, 155));
+ } else {
+ c.setBackground(new Color(235, 175, 235));
+ }
+ } else if (column>=table.getColumnCount()-extraFields.size()) {
+ if (isSelected) {
+ c.setBackground(new Color(155, 155, 155));
+ } else {
+ c.setBackground(new Color(225, 225, 225));
+ }
+ } else {
+ if (isSelected) {
+ c.setBackground(new Color(175, 175, 175));
+ } else {
+ c.setBackground(Color.white);
+ }
+ }
+
+ return c;
+ }
+ });
+
+
+ // Encapsulate table in a JScrollPane to allow scrolling
+ TableColumn column = null;
+ for (int i = 0; i < table.getColumnCount(); i++) {
+ column = table.getColumnModel().getColumn(i);
+ if (i == 0) {
+ column.setPreferredWidth(160);
+ } else {
+ column.setPreferredWidth(100);
+ }
+ }
+
+ TableRowSorter sorter = new TableRowSorter(table.getModel());
+ table.setRowSorter(sorter);
+
+ // Add MouseListener for onClick event
+ table.getTableHeader().addMouseListener(new MouseAdapter() {
+ private SortOrder currentOrder = SortOrder.UNSORTED;
+
+ @Override
+ public void mouseClicked(MouseEvent e) {
+ int column = table.getTableHeader().columnAtPoint(e.getPoint());
+ RowSorter sorter = table.getRowSorter();
+ List sortKeys = new ArrayList();
+ switch (currentOrder) {
+ case UNSORTED:
+ sortKeys.add(new RowSorter.SortKey(column, currentOrder = SortOrder.ASCENDING));
+ break;
+ case ASCENDING:
+ sortKeys.add(new RowSorter.SortKey(column, currentOrder = SortOrder.DESCENDING));
+ break;
+ case DESCENDING:
+ sortKeys.add(new RowSorter.SortKey(column, currentOrder = SortOrder.UNSORTED));
+ break;
+ }
+ sorter.setSortKeys(sortKeys);
+ }
+ });
+
+ table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
+
+ scroller = new JScrollPane(table);
+
+ JPanel tablePanel = new JPanel(new BorderLayout());
+
+
+ tablePanel.add(scroller,BorderLayout.CENTER);
+
+ // Add table to HashMap, and add tab to JTabbedPane
+ tableHash.put(name, table);
+ tabs.add(name, tablePanel);
+ }
+ }
+ }
+
+ // Load the TESSY batch files
+ // TODO: This whole functionality is a bit messy,
+ // if the program is closed during TESSy operations, this can go out of synch with the actual batch state in TESSy
+ @SuppressWarnings("unchecked")
+ private void loadBatchFiles(String name) {
+ File indataFile = new File(name+".batch");
+
+ if (indataFile.exists()) {
+ FileInputStream streamIn;
+ HashMap indata;
+ try {
+ streamIn = new FileInputStream(indataFile);
+ ObjectInputStream objectInputstream = new ObjectInputStream(streamIn);
+ indata = (HashMap) objectInputstream.readObject();
+ objectInputstream.close();
+ } catch (ClassNotFoundException | IOException e) {
+ e.printStackTrace();
+ indata = new HashMap();
+ }
+
+ // Add loaded batches and batch subjects to corresponding HashMaps
+ for (String k : indata.keySet()) {
+ TessyBatch bat = indata.get(k);
+ batches.put(bat.getBatchId(),bat);
+ batchSubjects.put(bat.getBatchId(), name);
+ }
+ }
+
+ }
+
+ // Load the isolate table
+ // Data is stored as key->value in a serialized ArrayList of HashMaps
+ // This is not very efficient but allows for updates of the metadata on the fly
+ @SuppressWarnings("unchecked")
+ private String[][] load(String name, String[] headers) {
+
+ File indataFile = new File(CONFIG_DIR+"/"+name+".dat");
+ ArrayList> indata;
+ if (indataFile.exists()) {
+ FileInputStream streamIn;
+ try {
+ streamIn = new FileInputStream(indataFile);
+ ObjectInputStream objectInputstream = new ObjectInputStream(streamIn);
+ indata = (ArrayList>) objectInputstream.readObject();
+ objectInputstream.close();
+ } catch (ClassNotFoundException | IOException e) {
+ e.printStackTrace();
+ indata = new ArrayList>();
+ }
+ } else {
+ indata = new ArrayList>();
+ }
+ String[][] data = new String[indata.size()][headers.length];
+ int i = 0;
+ for (HashMap row : indata) {
+ int j = 0;
+ for (String field : headers) {
+ if (row.containsKey(field)) {
+ data[i][j] = row.get(field);
+ }
+ j++;
+ }
+ i++;
+ }
+ return data;
+ }
+
+
+ // Load data import templates and add them to the quick import Menu
+ public void loadImportAndScheduleConfigs(JMenu menu) {
+
+ File dir = new File(CONFIG_DIR);
+ File[] files = dir.listFiles();
+
+ // Init empty HashMap of import configs
+ importConfigHash = new HashMap();
+ FILES: for (File f : files) {
+
+ if (f.getName().endsWith(".import")) {
+ // Derive the config name from the file name
+ String name = f.getName().substring(0,f.getName().length()-7);
+ try {
+ // Load the config and put it into a HashMap
+ ImportConfig cfg = ImportConfigHandler.loadConfig(f);
+ importConfigHash.put(name, cfg);
+ } catch (ClassNotFoundException | IOException e) {
+ JOptionPane.showMessageDialog(this,
+ "Config "+name+" could not be loaded from file: "+f.toString()+", the file may be corrupt or locked for reading.");
+ e.printStackTrace();
+ }
+
+ // Check if a corresponding item already exists in the Menu
+ for (int i = 0; i< menu.getItemCount();i++) {
+ if (menu.getItem(i) != null && menu.getItem(i).getText().equals(name)){
+ continue FILES;
+ }
+ }
+
+ // if it does not exists, add it to menu
+ JMenuItem csvImportItem = new JMenuItem(name);
+ csvImportItem.addActionListener(this);
+ csvImportItem.setActionCommand("IMPORT_"+name);
+ importQuickMenu.add(csvImportItem);
+ }
+ }
+ if (importConfigHash!=null && scheduler!=null) {
+ scheduler.setImportConfigHash(importConfigHash);
+ }
+
+ }
+
+ // Event handler
+ @Override
+ public void actionPerformed(ActionEvent e) {
+
+ // The importDialog opens an ImportConfigGUI and then loads the importConfigs when it is closed
+
+ if (e.getActionCommand().equals("importDialog")) {
+ if (importDialogWindow!=null) {
+ importDialogWindow.toFront();
+ return;
+ }
+ ImportConfigGUI importGui = new ImportConfigGUI(false);
+ importGui.setLocationRelativeTo(null);
+ importGui.init();
+ importDialogWindow = importGui;
+ importGui.addWindowListener(new WindowAdapter() {
+ @Override
+ // When the ImportConfigGUI window is closed, reload importConfigs
+ public void windowClosing(WindowEvent e) {
+ SwingUtilities.invokeLater(new Runnable(){
+
+ @Override
+ public void run() {
+ importDialogWindow = null;
+ loadImportAndScheduleConfigs(importQuickMenu);
+ }
+ });
+ }
+ });
+
+ // The config dialog opens a UploadConfigGUI and then loads the upload configs when the window is closed.
+ } else if (e.getActionCommand().equals("config")) {
+
+ if (uploadDialogWindow!=null) {
+ uploadDialogWindow.toFront();
+ return;
+ }
+
+ UploadConfigGUI uploadConfigGui = new UploadConfigGUI(false);
+ uploadConfigGui.setLocationRelativeTo(null);
+ uploadConfigGui.init();
+ uploadDialogWindow = uploadConfigGui;
+
+ uploadConfigGui.addWindowListener(new WindowAdapter() {
+ @Override
+ // When the config window is closed, reload upload configs
+ public void windowClosing(WindowEvent e) {
+ SwingUtilities.invokeLater(new Runnable(){
+
+ @Override
+ public void run() {
+ uploadDialogWindow = null;
+ loadUploadConfigs();
+ }
+ });
+ }
+ });
+
+ // Open job manager
+ } else if (e.getActionCommand().equals("viewJobs")) {
+ jobHandler.setVisible(true);
+ jobHandler.toFront();
+
+ // Save isolate table, note that it is also autosaved on many operations
+ } else if (e.getActionCommand().equals("save")) {
+ save();
+
+ // revert isolate table to saved version
+ } else if (e.getActionCommand().equals("revert")) {
+ revert();
+
+ // Add row to active isolate table
+ } else if (e.getActionCommand().equals("addrow")) {
+ addSingleRow();
+
+ // Add several rows to active isolate table
+ } else if (e.getActionCommand().equals("addrows")) {
+ addRowsInteractive();
+
+ // Link sequence data for selected rows
+ } else if (e.getActionCommand().equals("removerows")) {
+ String name= tabs.getTitleAt(tabs.getSelectedIndex());
+ JTable table = tableHash.get(name);
+ int num = table.getSelectedRowCount();
+ int reply = JOptionPane.showConfirmDialog(null, "This will delete "+Integer.toString(num)+" entries from the "+name+" table. Are you sure?", "Delete rows?", JOptionPane.YES_NO_OPTION);
+ if (reply == JOptionPane.YES_OPTION) {
+ removeRows();
+ }
+
+
+ // Link sequence data for selected rows
+ } else if (e.getActionCommand().equals("link")) {
+ EcdcLinkWorker worker = getEcdcLinkWorker(null, null);
+ worker.execute();
+
+ // Upload selected rows to ECDC SFTP, uses EcdcJob base class to perform in background.
+ } else if (e.getActionCommand().equals("uploadFtpSelected")) {
+ EcdcSftpUploadWorker worker = getSftpWorker(null, null);
+ if (worker!=null) {
+ worker.execute();
+ }
+
+ // Upload selected rows to configured systems, uses EcdcJob base class to perform in background.
+ } else if (e.getActionCommand().equals("uploadSelected")) {
+ EcdcFullUploadWorker worker = getFullUploadWorker(null, null);
+ if (worker!=null) {
+ worker.execute();
+ }
+
+ // Upload selected rows to ENA, uses EcdcJob base class to perform in background.
+ } else if (e.getActionCommand().equals("uploadEnaSelected")) {
+ EnaSubmissionWorker worker = getEnaSubmissionWorker(null, null);
+ if (worker!=null) {
+ worker.execute();
+ }
+
+ // Test selected rows at TESSy, uses EcdcJob base class to perform in background.
+ } else if (e.getActionCommand().equals("testSelected")) {
+ EcdcTessyCreateAndTestWorker worker = getEcdcTessyTestWorker(null, null);
+ if (worker!=null) {
+ worker.execute();
+ }
+
+ // Upload batch to TESSy, uses EcdcJob base class to perform in background.
+ } else if (e.getActionCommand().equals("uploadBatch")) {
+ EcdcTessyUploadWorker worker = getEcdcTessyUploadWorker(null);
+ if (worker!=null) {
+ worker.execute();
+ }
+
+ // Approve batch in TESSy, uses EcdcJob base class to perform in background.
+ } else if (e.getActionCommand().equals("approveBatch")) {
+ EcdcTessyApprovalWorker worker = getEcdcTessyApprovalWorker(null);
+ if (worker!=null) {
+ worker.execute();
+ }
+
+ // Reject batch in TESSy, uses EcdcJob base class to perform in background.
+ } else if (e.getActionCommand().equals("rejectBatch")) {
+ EcdcTessyRejectWorker worker = getEcdcTessyRejectWorker(null);
+ if (worker!=null) {
+ worker.execute();
+ }
+
+ // Quick import triggered from the Menu
+ } else if (e.getActionCommand().startsWith("IMPORT_")) {
+ String cfgName = e.getActionCommand().substring(7);
+
+ EcdcImportWorker worker = getEcdcImportWorker(cfgName);
+ if (worker!=null) {
+ worker.execute();
+ }
+ }
+ }
+
+
+ private void removeRows() {
+ String name= tabs.getTitleAt(tabs.getSelectedIndex());
+ JTable table = tableHash.get(name);
+ DefaultTableModel model = (DefaultTableModel)table.getModel();
+ int[] inds = table.getSelectedRows();
+ Integer[] indices = new Integer[inds.length];
+ int ii = 0;
+ for (int value : inds) {
+ indices[ii++] = Integer.valueOf(value);
+ }
+ Arrays.sort(indices, Collections.reverseOrder());
+ for(int i = 0; i < indices.length; i++)
+ {
+ indices[i] = table.convertRowIndexToModel(indices[i]);
+ model.removeRow(indices[i]);
+ }
+ }
+
+ // This functions adds a single row to the active isolate table
+ private void addSingleRow() {
+ String name= tabs.getTitleAt(tabs.getSelectedIndex());
+ JTable table = tableHash.get(name);
+ DefaultTableModel model = (DefaultTableModel)table.getModel();
+ model.addRow(new String[table.getColumnCount()]);
+ }
+
+ // This functions queries the user for the number of rows and then adds them to the active isolate table
+ private void addRowsInteractive() {
+ String s = (String)JOptionPane.showInputDialog(
+ this,
+ "Choose number of rows to add",
+ "Add rows",
+ JOptionPane.PLAIN_MESSAGE
+ );
+
+ if (s==null) {
+ return;
+ }
+ int num;
+ try {
+ num = Integer.parseInt(s);
+ } catch (Exception e2) {
+ e2.printStackTrace();
+ return;
+ }
+ String name= tabs.getTitleAt(tabs.getSelectedIndex());
+ JTable table = tableHash.get(name);
+ DefaultTableModel model = (DefaultTableModel)table.getModel();
+ for (int i = 0;i box = new JComboBox();
+ for (String id : batches.keySet()) {
+ TessyBatch bat = batches.get(id);
+ if (bat.getState().matches(batchState)) {
+ box.addItem(id+" ("+bat.getState()+")");
+ }
+ }
+ box.setSelectedIndex(box.getItemCount()-1);
+ int res = JOptionPane.showConfirmDialog( null, box, "Select batch", JOptionPane.OK_CANCEL_OPTION);
+ String choice = (String)box.getSelectedItem();
+ if (res != JOptionPane.OK_OPTION || choice==null) {
+ return null;
+ }
+ return(choice.split(" ")[0]);
+ }
+
+
+ // This function is interactive if the argument is null, otherwise it will use the supplied batchId
+ // It creates an EcdcJob for rejecting a batch in TESSy
+ public EcdcTessyRejectWorker getEcdcTessyRejectWorker(String batchId) {
+
+ // Interactive dialog for selecting batch to reject
+ if (batchId==null) {
+ batchId = getBatchIdInteractive("VALIDATED");
+ if (batchId==null) {
+ return null;
+ }
+ }
+
+
+ // Get batch
+ TessyBatch batch = batches.get(batchId);
+ if (batch==null) {
+ JOptionPane.showMessageDialog(this, "No such batch "+batchId+" found!");
+ return null;
+ }
+ String batchSubject = batchSubjects.get(batchId);
+
+ // Create and return job, note that it is not executed automatically
+ EcdcTessyRejectWorker worker = new EcdcTessyRejectWorker(this, batch, batchSubject, null, null, null);
+ return worker;
+ }
+
+
+
+ // This function is interactive if the argument is null, otherwise it will use the supplied batchId
+ // It creates an EcdcJob for approving a batch in TESSy
+ public EcdcTessyApprovalWorker getEcdcTessyApprovalWorker(String batchId) {
+
+ // Interactive dialog for selecting batch to reject
+ if (batchId==null) {
+ batchId = getBatchIdInteractive("VALIDATED");
+ if (batchId==null) {
+ return null;
+ }
+ }
+
+ // Get batch
+ TessyBatch batch = batches.get(batchId);
+ if (batch==null) {
+ JOptionPane.showMessageDialog(this, "No such batch "+batchId+" found!");
+ return null;
+ }
+ String batchSubject = batchSubjects.get(batchId);
+
+ // Create and return job, note that it is not executed automatically
+ EcdcTessyApprovalWorker worker = new EcdcTessyApprovalWorker(this,batch,batchSubject,null,null,null);
+ return worker;
+ }
+
+ // This function is interactive if the argument is null, otherwise it will use the supplied batchId
+ // It creates an EcdcJob for uploading a batch to TESSy
+ public EcdcTessyUploadWorker getEcdcTessyUploadWorker(String batchId) {
+
+ // Interactive dialog for selecting batch to reject
+ if (batchId==null) {
+ batchId = getBatchIdInteractive("TESTED");
+ if (batchId==null) {
+ return null;
+ }
+ }
+
+ // Get batch
+ TessyBatch batch = batches.get(batchId);
+ if (batch==null) {
+ JOptionPane.showMessageDialog(this, "No such batch "+batchId+" found!");
+ return null;
+ }
+ String batchSubject = batchSubjects.get(batchId);
+
+ // Create and return job, note that it is not executed automatically
+ EcdcTessyUploadWorker worker = new EcdcTessyUploadWorker(this,batch,batchSubject,null,null,null);
+ return worker;
+ }
+
+
+ // This function uses the current table selection if the arguments are null, otherwise it will use the supplied table and rows
+ // It creates an EcdcJob for generating a batch object and testing it in TESSy
+ // This is a requirement before upload in this application
+ public EcdcTessyCreateAndTestWorker getEcdcTessyTestWorker(String tabname, int[] selected) {
+ if (tabname==null) {
+ // If not supplied, use active tab
+ tabname = tabs.getTitleAt(tabs.getSelectedIndex());
+ }
+ JTable table = tableHash.get(tabname);
+
+ // If not supplied, use the selected rows.
+ boolean interactive=false;
+ if (selected==null) {
+ interactive = true;
+ selected = getUploadRows(table,true);
+ }
+
+
+ ArrayList rows = new ArrayList();
+
+ int tessyBatchColumn = getColumn(table,TESSY_BATCH_COLUMN);
+ if (tessyBatchColumn==-1) {
+ return null;
+ }
+
+ // Check which isolates that have been submitted to TESSy before and separate them into different lists
+ // depending on whether they are approved or not. Resubmitting isolates that are already in progress in TESSy will not work.
+ ArrayList rowsOld = new ArrayList();
+ ArrayList rowsInProgress = new ArrayList();
+ for (int i : selected) {
+ if(table.getModel().getValueAt(i,0)==null || table.getModel().getValueAt(i,0).equals("")) {
+ continue;
+ }
+ if (table.getModel().getValueAt(i,tessyBatchColumn)==null || table.getModel().getValueAt(i,tessyBatchColumn).equals("") || ((String)table.getModel().getValueAt(i,tessyBatchColumn)).endsWith("T") || ((String)table.getModel().getValueAt(i,tessyBatchColumn)).endsWith("R")) {
+ rows.add(i);
+ } else if (((String)table.getModel().getValueAt(i,tessyBatchColumn)).endsWith("A")) {
+ rowsOld.add(i);
+ } else {
+ rowsInProgress.add(i);
+ }
+ }
+
+
+ if (interactive) {
+
+ // For interactive submission, give the user an error that the selection
+ // includes isolates that have been uploaded but not approved nor rejected.
+ if (!rowsInProgress.isEmpty()) {
+ JOptionPane.showMessageDialog(this,
+ "Selection includes isolates that are parts of batches that have been uploaded but not approves, these cannot be included. Try again.");
+ return null;
+ }
+
+ // If the selection includes isolates that have been approved before, warn the user that they will be replaced.
+ if (!rowsOld.isEmpty()) {
+ int reply = JOptionPane.showConfirmDialog(null, "Some isolates have already been approved in TESSy, include them anyway?", "Resubmit isolates?", JOptionPane.YES_NO_OPTION);
+ if (reply == JOptionPane.YES_OPTION) {
+ rows.addAll(rowsOld);
+ }
+ }
+ } else {
+ // For non-interactive session, quietly resubmit old approved or rejected isolates but never include those in the uploaded state.
+ rows.addAll(rowsOld);
+ }
+
+ // If no isolates are included, warn if interactive and then return.
+ if (rows.isEmpty()) {
+ if (interactive) {
+ JOptionPane.showMessageDialog(this, "No isolates selected for submission.");
+ }
+ return null;
+ }
+
+ // Set TESSy credentials and options for the batch
+ UploadConfig cfg = configHash.get(tabname);
+ TessyCredentials cred = cfg.getTessyCredentials();
+ TessyBatch batch = new TessyBatch("",cfg.getTessyCountry(),cfg.getTessyContact(), cfg.getTessyProvider(), cfg.getTessyMeta(), cfg.getTessySubject());
+ batch.setTessyCredentials(cred);
+ batch.setHaltWarn(cfg.getTessyHaltWarn());
+ batch.setHaltRemark(cfg.getTessyHaltRemark());
+
+ // TODO: This is a bit of a hack. Basically the code creates a table with headers, the first four columns are fixed,
+ // RecordId, DateUsedForStatistics, EnaId, Assembly file. The rest are any supplied fields in the data table.
+ // The code can be improved
+ int extrafields = 4;
+
+ String[] headers = new String[table.getColumnCount()-extraFields.size()+extrafields];
+ String[][] data = new String[rows.size()][table.getColumnCount()-extraFields.size()+extrafields];
+
+ int enaColumn = getColumn(table,ENA_RUN_COLUMN);
+ int assemblyColumn = getColumn(table,ASSEMBLY_FILE_COLUMN);
+
+ headers[0] = "id";
+ headers[1] = "DateUsedForStatistics";
+ headers[2] = "WgsEnaId";
+ headers[3] = "WgsAssembly";
+ for (int j = extrafields ;j uploadList = new ArrayList();
+ ArrayList nonUploadList = new ArrayList();
+ int uploadColumn = getColumn(table,UPLOAD_COLUMN);
+ for (int i : inds) {
+ int modelInd = table.convertRowIndexToModel(i);
+ String uploadVal = (String)table.getModel().getValueAt(modelInd, uploadColumn);
+ if (uploadVal!=null && uploadVal.equals("Yes")) {
+ uploadList.add(modelInd);
+ } else {
+ nonUploadList.add(modelInd);
+ }
+ }
+ if (interactive && nonUploadList.size()>0) {
+ int reply = JOptionPane.showConfirmDialog(null, "The selection includes "+Integer.toString(nonUploadList.size())+" isolates not flagged as 'Ready for upload', change the flag and upload?", "Submit?", JOptionPane.YES_NO_OPTION);
+ if (reply == JOptionPane.YES_OPTION) {
+ for (int i : nonUploadList) {
+ table.getModel().setValueAt("Yes",i, uploadColumn);
+ }
+ uploadList.addAll(nonUploadList);
+ }
+ }
+
+
+ int[] out = new int[uploadList.size()];
+ for (int i = 0;i rows = new ArrayList();
+
+ // If not supplied, use selected rows
+ if (selectedRows==null) {
+ selectedRows = getUploadRows(dataTable, true);
+ }
+
+ // Check that the selected rows have a recordId and raw read files, and that they do not already have an ENA run id.
+ for (int i : selectedRows) {
+ if (dataTable.getModel().getValueAt(i, recordIdCol)!=null && !dataTable.getModel().getValueAt(i, recordIdCol).equals("") && dataTable.getModel().getValueAt(i, filesCol)!=null && !dataTable.getModel().getValueAt(i, filesCol).equals("") && (dataTable.getModel().getValueAt(i, enaColumn)==null || dataTable.getModel().getValueAt(i, enaColumn).equals(""))) {
+ rows.add(i);
+ }
+ }
+
+ // return if nothing to do
+ if (rows.isEmpty()) {
+ JOptionPane.showMessageDialog(this,
+ "No valid entries selected for submission to ENA.");
+ return null;
+ }
+
+
+ // Create a table with the required data for ENA submission
+ String[] headers = {"id","RecordId","Year","Files","WgsProtocol"};
+ String[][] data = new String[rows.size()][headers.length];
+ int c = 0;
+ for (int i : rows) {
+
+ // Get year from supplied dates, if no date is supplied, skip.
+ String year = EcdcUtils.calcDate(dataTable,i,"yyyy");
+ if (year.equals("")) {
+ JOptionPane.showMessageDialog(this,
+ "No date found for one or more entries, ENA submission halted.");
+ return null;
+ }
+
+ data[c][0]=Integer.toString(i);
+ data[c][1]=(String)dataTable.getModel().getValueAt(i, recordIdCol);
+ data[c][2]=year;
+ data[c][3]=(String)dataTable.getModel().getValueAt(i, filesCol);
+ data[c][4]=(String)dataTable.getModel().getValueAt(i, wgsProtocolCol);
+ c++;
+ }
+
+
+ // Create and return, an EcdcJob for submitting to ENA. It does not automatically run.
+ EnaSubmissionWorker worker = new EnaSubmissionWorker(this,null,tabname,configHash.get(tabname),headers,data);
+
+ // Set table column for worker to output ENA accession in
+ worker.setEnaColumn(enaColumn);
+ worker.setOutputHandler(new JTableOutputHandler(dataTable));
+ return worker;
+ }
+
+
+ // This function uses the current table selection if the arguments are null, otherwise it will use the supplied table and rows
+ // It creates an EcdcJob for uploading to all configured systems
+ public EcdcFullUploadWorker getFullUploadWorker(String tabname, int[] selected) {
+
+ // Use selected table if argument is not supplied
+ if (tabname==null) {
+ tabname = tabs.getTitleAt(tabs.getSelectedIndex());
+ }
+ JTable table = tableHash.get(tabname);
+
+ // use selected rows if not supplied
+ if (selected==null) {
+ selected = getUploadRows(table, true);
+ }
+
+ // Create and return an EcdcJob for submitting to all configured systems. I still needs to be executed
+ EcdcFullUploadWorker worker = new EcdcFullUploadWorker(this,null,tabname, configHash.get(tabname),null,null);
+ worker.setRows(selected);
+ return worker;
+ }
+
+ // This function uses the current table selection if the arguments are null, otherwise it will use the supplied table and rows
+ // It creates an EcdcJob for uploading to SFTP
+ public EcdcSftpUploadWorker getSftpWorker(String tabname, int[] selected) {
+
+ // Use selected table if argument is not supplied
+ if (tabname==null) {
+ tabname = tabs.getTitleAt(tabs.getSelectedIndex());
+ }
+ JTable table = tableHash.get(tabname);
+
+ // use selected rows if not supplied
+ if (selected==null) {
+ selected = getUploadRows(table, true);
+ }
+
+ // Create a data table with information needed for SFTP upload
+ int n = selected.length;
+ String[] headers = new String[6];
+ String[][] data = new String[n][6];
+
+ int recordIdCol = 0;
+ int filesCol = getColumn(table, READ_FILES_COLUMN);
+ int assemblyCol = getColumn(table, ASSEMBLY_FILE_COLUMN);
+ int tessyIdCol = getColumn(table, TESSY_ID_COLUMN);
+ int protocolCol = getColumn(table, "WgsProtocol");
+ int uiCol = getColumn(table, UI_COLUMN);
+ int ftpCol = getColumn(table, SFTP_COLUMN);
+
+
+ int count = 0;
+ for (int i : selected) {
+ data[count][0] = Integer.toString(i);
+ data[count][1] = (String)table.getModel().getValueAt(i, recordIdCol);
+ data[count][2] = (String)table.getModel().getValueAt(i, filesCol);
+
+ String assemblyFiles = (String)table.getModel().getValueAt(i, assemblyCol);
+ if (assemblyFiles!=null && !assemblyFiles.equals("")) {
+ if (data[count][2]==null) {
+ data[count][2] = "";
+ }
+ if (!data[count][2].equals("")) {
+ data[count][2] = data[count][2]+";";
+ }
+ data[count][2] = data[count][2] + assemblyFiles;
+ }
+ data[count][3] = (String)table.getModel().getValueAt(i, tessyIdCol);
+ data[count][4] = (String)table.getModel().getValueAt(i, protocolCol);
+ data[count][5] = (String)table.getModel().getValueAt(i, uiCol);
+
+ count++;
+ }
+
+ if (count==0) {
+ JOptionPane.showMessageDialog(this,
+ "No valid entries selected for submission using SFTP.");
+ return null;
+ }
+
+ // Create and return a worker for SFTP upload. It still needs to be executed
+ EcdcSftpUploadWorker worker = new EcdcSftpUploadWorker(this,null,tabname,configHash.get(tabname),headers,data);
+
+ // Set output handling for this worker
+ worker.setFtpColumn(ftpCol);
+ worker.setOutputHandler(new JTableOutputHandler(table));
+
+ return worker;
+ }
+
+
+ // Save isolate table and batch data
+ void save() {
+
+ // Iterate over all tabs
+ int totalTabs = tabs.getTabCount();
+ for(int i = 0; i < totalTabs; i++)
+ {
+ ArrayList> data = new ArrayList>();
+ String name= tabs.getTitleAt(i);
+ System.out.println(name);
+ File outFile = new File(name+".dat");
+ File outFileBatch = new File(name+".batch");
+
+ // Get the batches associated with this subject
+ // TODO: This can be done more efficiently
+ HashMap saveBatches = new HashMap();
+ for (String k : batches.keySet()) {
+ if (batchSubjects.get(k).equals(name)) {
+ TessyBatch bat = batches.get(k);
+ if (!bat.getState().equals("APPROVED")) {
+ saveBatches.put(k,bat);
+ }
+ }
+ }
+
+ // Iterate through the data table, add all non-empty rows to the List for saving
+ JTable table = tableHash.get(name);
+ int rows = table.getRowCount();
+ int cols = table.getColumnCount();
+ for (int j = 0; j< rows; j++) {
+ HashMap row=null;
+ boolean save = false;
+
+ for (int k = 0; k< cols; k++) {
+ String val = (String)table.getModel().getValueAt(j, k);
+ if (val!=null) {
+ if (!save) {
+ save = true;
+ row = new HashMap();
+ }
+ row.put((String)table.getColumnName(k), (String)table.getModel().getValueAt(j, k));
+ }
+ }
+ if (save) {
+ data.add(row);
+ }
+ }
+
+ // Write table data to file
+ FileOutputStream foutData;
+ try {
+ foutData = new FileOutputStream(outFile);
+ ObjectOutputStream oos = new ObjectOutputStream(foutData);
+ oos.writeObject(data);
+ oos.close();
+ } catch (IOException e1) {
+ JOptionPane.showMessageDialog(this,
+ "Save failed.");
+ e1.printStackTrace();
+ }
+
+ // Write batch data to file
+ FileOutputStream foutBatch;
+ try {
+ foutBatch = new FileOutputStream(outFileBatch);
+ ObjectOutputStream oosB = new ObjectOutputStream(foutBatch);
+ oosB.writeObject(saveBatches);
+ oosB.close();
+ } catch (IOException e1) {
+ JOptionPane.showMessageDialog(this,
+ "Saving TESSy batch data failed.");
+ e1.printStackTrace();
+ }
+
+ }
+
+ }
+
+
+ // This function uses the current table selection if the arguments are null, otherwise it will use the supplied table and rows
+ // it links sequence read files and assembly files using the RecordId and the UploadConfig file pattern
+ public EcdcLinkWorker getEcdcLinkWorker(String name, int[] selected) {
+
+ // Get the selected table if not supplied
+ if (name==null) {
+ name= tabs.getTitleAt(tabs.getSelectedIndex());
+ }
+
+ JTable table = tableHash.get(name);
+
+ // A config is required,
+ if (!configHash.containsKey(name)) {
+ JOptionPane.showMessageDialog(this, "No upload configuration found for subject "+name+", cannot link data.");
+ return null;
+ }
+ UploadConfig cfg = configHash.get(name);
+
+
+ EcdcLinkWorker linkWorker = new EcdcLinkWorker(this, null, name, cfg, null, null);
+ linkWorker.setColumnNames(BASE_FILE_COLUMN, READ_FILES_COLUMN, ASSEMBLY_FILE_COLUMN);
+ linkWorker.setTable(table);
+ linkWorker.setSelected(selected);
+ return linkWorker;
+
+ }
+
+
+ // This functions handles the TESSy validation results output, puts them into the isolate table.
+ public void setValidationResults(String name, String batchId, String suffix, String dateStr, String appDateStr, boolean setId) {
+
+ HashMap results = batches.get(batchId).getValidationResults();
+
+ batchId = batchId+suffix;
+
+ JTable table = tableHash.get(name);
+
+ // Init column indices
+ int tessyValColumn = getColumn(table,TESSY_VALIDATION_COLUMN);
+ int tessyTestColumn = getColumn(table,TESSY_TEST_COLUMN);
+ int tessyBatchColumn = getColumn(table,TESSY_BATCH_COLUMN);
+ int tessyUploadColumn = getColumn(table,TESSY_UPLOADED_COLUMN);
+ int tessyApproveColumn = getColumn(table,TESSY_APPROVED_COLUMN);
+ int tessyIdColumn = getColumn(table,TESSY_ID_COLUMN);
+
+ // TESSy validation column required
+ if (tessyValColumn==-1) {
+ return;
+ }
+
+
+ // Clear all old non-uploaded batches, only one test batch can be active at any time
+ for (String k : tableHash.keySet()) {
+ JTable t = tableHash.get(k);
+ for (int i = 0; i generateBox(String[] options)
+ {
+ JComboBox bx=null;
+ bx=new JComboBox();
+ bx.addItem("");
+ for (String option: options) {
+ bx.addItem(option);
+ }
+ bx.setEditable(false);
+
+ return bx;
+
+ }
+
+ // Get the column index for a certain table and column name, returns -1 if not found
+ int getColumn(JTable table, String colname) {
+ for (int i = 0;i getUploadList(JTable table, UploadConfig cfg) {
+
+
+ ArrayList inds = new ArrayList();
+ int uploadColumn = getColumn(table,UPLOAD_COLUMN);
+ int readsColumn = getColumn(table,READ_FILES_COLUMN);
+ int assemblyColumn = getColumn(table,ASSEMBLY_FILE_COLUMN);
+ int modifiedColumn = getColumn(table,MODIFIED_COLUMN);
+ int tessyApprovedColumn = getColumn(table,TESSY_APPROVED_COLUMN);
+ int sftpColumn = getColumn(table,SFTP_COLUMN);
+ int enaColumn = getColumn(table,ENA_RUN_COLUMN);
+
+ for (int i = 0;i searchDev(File file, ArrayList foundFiles) throws IOException {
+ System.out.println("Reading directory: "+file.getName());
+
+ /*FileFilter filter = new FileFilter() {
+ @Override public boolean accept(File f)
+ {
+ String name = f.getName();
+ return (name.endsWith(".fastq.gz") || name.endsWith(".fastq") || name.endsWith(".fasta") || name.endsWith(".fasta.gz") || f.isDirectory());
+ }
+ };*/
+
+
+ System.out.println("Reading files");
+ //File[] files = file.listFiles(filter);
+ // System.out.println("Iterating over "+Integer.toString(files.length)+" files");
+
+ DirectoryStream.Filter filter = new DirectoryStream.Filter() {
+ @Override
+ public boolean accept(Path entry) throws IOException {
+ String name = entry.getFileName().toString();
+ return (name.endsWith(".fastq.gz") || name.endsWith(".fastq") || name.endsWith(".fasta") || name.endsWith(".fasta.gz") || Files.isDirectory(entry));
+
+ }
+ };
+
+ Path dir = file.toPath();
+ DirectoryStream stream = Files.newDirectoryStream( dir , filter);
+ for (Path path : stream) {
+ File f = path.toFile();
+ if (f.isDirectory()) {
+ foundFiles = searchDev(f,foundFiles);
+ } else {
+ foundFiles.add(f);
+ }
+ }
+ stream.close();
+
+ return foundFiles;
+ }
+
+ // This utility function recursively searches a file system for sequence files
+ public static ArrayList search(File file, ArrayList foundFiles) throws IOException {
+ System.out.println("Reading directory: "+file.getName());
+
+ FileFilter filter = new FileFilter() {
+ @Override public boolean accept(File f)
+ {
+ String name = f.getName();
+ return (name.endsWith(".fastq.gz") || name.endsWith(".fastq") || name.endsWith(".fasta") || name.endsWith(".fasta.gz") || f.isDirectory());
+ }
+ };
+
+
+ System.out.println("Reading files");
+ File[] files = file.listFiles(filter);
+ System.out.println("Iterating over "+Integer.toString(files.length)+" files");
+
+
+
+ for (File f : files) {
+ if (f.isDirectory()) {
+ foundFiles = search(f,foundFiles);
+ } else {
+ foundFiles.add(f);
+ }
+ }
+
+ return foundFiles;
+ }
+
+}
diff --git a/src/eu/europa/ecdc/enauploader/EnaSubmissionWorker.java b/src/eu/europa/ecdc/enauploader/EnaSubmissionWorker.java
new file mode 100644
index 0000000..2fd680e
--- /dev/null
+++ b/src/eu/europa/ecdc/enauploader/EnaSubmissionWorker.java
@@ -0,0 +1,291 @@
+package eu.europa.ecdc.enauploader;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.UUID;
+
+// This class submits sequence data and minimal metadata (year, country, sequencing instrument) to ENA
+public class EnaSubmissionWorker extends EcdcJob {
+
+ private static final int EXPECTED_FIELDS = 5;
+ private int enaColumn;
+ private OutputHandler outputHandler;
+
+ // Mandatory constructor
+ EnaSubmissionWorker(EcdcUploaderGUI gui, TessyBatch batch, String name, UploadConfig cfg, String[] headers,
+ String[][] data) {
+ super(gui, batch, name, cfg, headers, data);
+
+ }
+
+ // Set output handler object
+ public void setOutputHandler(OutputHandler handler) {
+ this.outputHandler = handler;
+ }
+
+ // Set table column for output to GUI
+ public void setEnaColumn(int col) {
+ enaColumn = col;
+ }
+
+
+ // Main routine for the job
+ @Override
+ protected Object doInBackground() {
+
+ // Get all config info
+ String studyAcc = cfg.getEnaProjectAcc();
+ String center = cfg.getEnaCenter();
+ String checklist = cfg.getEnaChecklist();
+ String login = cfg.getEnaLogin();
+ String organism = cfg.getOrganism();
+ char[] password = cfg.getEnaPassword();
+ boolean anonymize = cfg.getEnaAnonymize();
+ boolean prod = cfg.getEnaProd();
+ System.out.println("PROD: "+Boolean.toString(prod));
+
+ String ftpHost = cfg.getEnaFtpHost();
+ String curlPath = cfg.getCurlPath();
+ String tmpPath = cfg.getTmpPath();
+
+ // Log parameters used
+ log("ENA parameters");
+ log("Study: "+studyAcc);
+ log("Center: "+center);
+ log("Checklist: "+checklist);
+ log("Login: "+login);
+ log("Organism: "+organism);
+ log("Anonymize: "+anonymize);
+ log("ENA prodcution: "+prod);
+ log("FTP: "+ftpHost);
+ log("CURL: "+curlPath);
+ log("TMP: "+tmpPath);
+
+ log("Starting...");
+ setTitle("Uploading to ENA study "+studyAcc);
+ setStatus("Checking configs...");
+ setProgress(5);
+
+ // Check that all required config fields are there
+ if (password.length==0 || studyAcc.equals("") || organism.equals("") || center.equals("") || login.equals("") || checklist.equals("")) {
+ log("Organism, Study accession, Center, Login, Password, and Checklist are mandatory fields for ENA submission.");
+ log("Aborting ENA submission.");
+
+ setStatus("Error, missing fields in config");
+ log("Config is missing information, aborting.");
+ setProgress(10);
+ return null;
+ }
+
+ // Check that there is data to submit
+ if (data == null || data.length==0) {
+ log("There are no data to submit.");
+ log("Aborting ENA submission.");
+
+ setStatus("Error, no data selected for submission");
+ log("No data, aborting.");
+ setProgress(10);
+ return null;
+ }
+
+ // Check that data has the correct format
+ if (headers.length!=EXPECTED_FIELDS || data[0].length!=EXPECTED_FIELDS) {
+ log("Unexpected number of columns in data or headers. Expected number: "+Integer.toString(EXPECTED_FIELDS));
+ log("Aborting ENA submission.");
+
+ setStatus("Error, data formatting not recongnized");
+ log("Data formatting error");
+ setProgress(10);
+ return null;
+ }
+
+ int failed = 0;
+ int success = 0;
+ int increment = 75/data.length;
+ int total = data.length;
+
+ if (total==0) {
+ setStatus("Error, no valid entries selected");
+ setProgress(20);
+ return null;
+ }
+
+ // Iterate over each isolate entry
+ ISOLATE: for (int i = 0;i= jTable1.getRowCount()) {
+ ((DefaultTableModel)jTable1.getModel()).addRow(new String[jTable1.getColumnCount()]);
+ }
+ rowstring=st1[i];
+ if (rowstring.equals("")) {
+ break;
+ }
+ //StringTokenizer st2=new StringTokenizer(rowstring,"\t");
+ String[] st2=rowstring.split("\t",-1);
+
+ for(int j=0;jGENOMIC\n");
bw.write("RANDOM \n");
bw.write("\n");
- if (instrument.toLowerCase().matches(".*illumina.*")) {
+ if (instrument.toLowerCase().matches(".*illumina.*") || instrument.toLowerCase().matches(".*nextseq.*")) {
bw.write("\n");
} else {
bw.write(" \n");
diff --git a/src/eu/europa/ecdc/enauploader/GhostEntryCreator.java b/src/eu/europa/ecdc/enauploader/GhostEntryCreator.java
new file mode 100644
index 0000000..cd3953b
--- /dev/null
+++ b/src/eu/europa/ecdc/enauploader/GhostEntryCreator.java
@@ -0,0 +1,624 @@
+package eu.europa.ecdc.enauploader;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Base64;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.UUID;
+
+import javax.swing.JComboBox;
+import javax.swing.JFrame;
+import javax.swing.JPanel;
+
+
+// This is a utility class for creating Ghost entries in a Bionumerics database
+public class GhostEntryCreator extends JFrame {
+
+
+
+
+ private static HashMap usedIds;
+
+ public static void main(String[] args) {
+ try {
+ Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
+ } catch (ClassNotFoundException e) {
+ e.printStackTrace();
+ }
+
+
+ /* if (args.length<5) {
+ System.out.println("Usage: createghostentry ");
+ System.exit(0);
+ }
+
+ String insubj = args[0].toUpperCase();
+ String environ = args[1].toUpperCase();
+ String username = args[2];
+ String password = args[3];
+ String infile = args[4];
+ */
+
+ String insubj = "LISTISO";
+ String environ = "PROD";
+ String username = "ealm";
+
+
+ ArrayList BNfields = new ArrayList();
+ BNfields.add("BN_ANN1");
+ BNfields.add("BN_ANN2");
+ BNfields.add("BN_ANN3");
+ BNfields.add("BN_WGS_ECDCID");
+ String sourceDb = "jdbc:sqlite:Z:/BioNumerics/Data/ELITE_WGS/ELITE_WGS.sqlite";
+ String transferlist = "C:/Users/ealm/Desktop/transfer3.csv";
+ String targetDb;
+ String targetTable;
+ String password;
+
+ if (environ.equals("PROD")) {
+ targetDb = "jdbc:sqlserver://nsql3:1433;DatabaseName=Molsurv;integratedSecurity=true";
+ targetTable = "vSRC_"+insubj+"_ENTRYTABLE";
+ password = "hbj024";
+ } else {
+ targetDb = "jdbc:sqlserver://zdevsql16.idmdevdmz.local:1433;DatabaseName=BNAS_TEST_"+insubj;
+ targetTable = "ENTRYTABLE";
+ password = "Solna123";
+ }
+
+
+
+
+
+
+ String infile = "C:/Users/ealm/Desktop/testghost.txt";
+
+ try {
+ dumpDbToFile(sourceDb, infile);
+ } catch (SQLException | IOException e1) {
+
+ e1.printStackTrace();
+ System.exit(0);
+ }
+
+
+ ArrayList isolates = new ArrayList();
+ try {
+ BufferedReader br = new BufferedReader(new FileReader(transferlist));
+ String line;
+ while ((line=br.readLine())!=null) {
+ if (line.split(",")[1].toUpperCase().equals("Y")) {
+ isolates.add(line.split(",")[0]);
+ }
+
+ }
+ br.close();
+
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+
+
+
+ try {
+ boolean success = upload(insubj, environ, username, password, infile, BNfields, isolates, targetDb, targetTable);
+ if (success) {
+ System.out.println("Submission successful.");
+ } else {
+ System.out.println("Submission failed.");
+ }
+ } catch (Exception e) {
+
+ e.printStackTrace();
+ }
+
+ }
+
+ private static void dumpDbToFile(String sourceDb, String infile) throws SQLException, IOException {
+
+ BufferedWriter bw = new BufferedWriter(new FileWriter(infile));
+
+ Connection conn2 = DriverManager.getConnection(sourceDb);
+ Statement m_Statement2 = conn2.createStatement();
+ System.out.println("Connection to SQLite has been established.");
+ String query2 = "SELECT * FROM entrytable";
+
+
+ ResultSet rs2 = m_Statement2.executeQuery(query2);
+ for (int i = 1; i < rs2.getMetaData().getColumnCount() + 1; i++) {
+ String val = rs2.getMetaData().getColumnName(i);
+ val = val.replaceAll("\t","");
+ if (i>1) {
+ bw.write("\t");
+ }
+ bw.write(val);
+ }
+
+ while (rs2.next()) {
+ bw.write("\n");
+ for (int i = 1; i < rs2.getMetaData().getColumnCount() + 1; i++) {
+ String val = rs2.getString(i);
+ if (val==null) {
+ val = "";
+ }
+ val = val.replaceAll("\t","");
+ if (i>1) {
+ bw.write("\t");
+ }
+ bw.write(val);
+ }
+
+ }
+ bw.close();
+ conn2.close();
+ }
+
+ private static boolean upload(String subj, String environ, String login, String password, String infile, ArrayList BNfields, ArrayList isolates, String targetDb, String targetTable) throws Exception {
+
+
+ String baseUrl;
+
+ if (environ.equals("TEST")) {
+ baseUrl = "http://zdevbion16.idmdevdmz.local/bnwebservice/";
+ } else if (environ.equals("PROD")) {
+ baseUrl = "http://zbionsts.ecdcdmz.europa.eu/bnwebservice/";
+ } else {
+ System.out.println("Only TEST or PROD are allowed for the second argument");
+ return false;
+ }
+
+ File xmlFile = new File("C:/Users/ealm/test3.txt");
+
+
+ int numrecords = writeXml(xmlFile,new File(infile),subj, BNfields, isolates, targetDb, targetTable);
+ if (numrecords == 0) {
+ return false;
+ }
+ //System.exit(0);
+
+
+ System.out.print("Number of records read from list: ");
+ System.out.println(isolates.size());
+ System.out.print("Number of records loaded into xml: ");
+ System.out.println(numrecords);
+ //System.exit(0);
+ //TODO:
+
+ URL url = new URL(baseUrl+"logon.aspx?userid="+login+"&pwd="+password+"&db=BNAS_"+environ+"_"+subj);
+ final long startTime = System.nanoTime();
+
+ HttpURLConnection con = (HttpURLConnection) url.openConnection();
+ con.setRequestMethod("GET");
+
+
+ int status = con.getResponseCode();
+ BufferedReader in = new BufferedReader(
+ new InputStreamReader(con.getInputStream()));
+ String inputLine;
+ StringBuffer content = new StringBuffer();
+ while ((inputLine = in.readLine()) != null) {
+ content.append(inputLine);
+ }
+ in.close();
+ con.disconnect();
+ System.out.println(status);
+ System.out.println(content);
+
+ String sessionId = "";
+ if (content.toString().startsWith("OK")) {
+ sessionId = content.toString().substring(3);
+ } else {
+ return false;
+ }
+ System.out.println(sessionId);
+ final long interTime = System.nanoTime();
+
+ //Code here
+ //runscript.aspx?session=" + SessionId + "&name=" + scriptName + "&arg=" + UrlCodecHelper.Encode(arg);
+
+ String scriptName = "UploadToBnServer.py";
+ String arg = "";
+ String base64Xml = "";
+ String xml = "";
+
+
+ BufferedReader br = new BufferedReader(new FileReader(xmlFile));
+ String l;
+ while ((l=br.readLine())!=null) {
+ xml = xml+ l;
+ }
+ br.close();
+ base64Xml = Base64.getEncoder().encodeToString(xml.getBytes());
+
+
+ url = new URL(baseUrl+"runscript.aspx?session="+sessionId+"&name="+scriptName+"&arg="+arg);
+
+
+ con = (HttpURLConnection) url.openConnection();
+ con.setRequestMethod("POST");
+
+ con.setDoOutput(true);
+ DataOutputStream wr = new DataOutputStream(con.getOutputStream());
+ wr.writeBytes(base64Xml);
+ wr.flush();
+ wr.close();
+
+ status = con.getResponseCode();
+ in = new BufferedReader(
+ new InputStreamReader(con.getInputStream()));
+
+ content = new StringBuffer();
+ while ((inputLine = in.readLine()) != null) {
+ content.append(inputLine);
+ }
+ in.close();
+ con.disconnect();
+ System.out.println(status);
+ System.out.println(content);
+ boolean success = true;
+ if (!content.toString().startsWith("OK")) {
+ success = false;
+ }
+
+ final long interTime2 = System.nanoTime();
+
+
+ url = new URL(baseUrl+"logoff.aspx?session="+sessionId);
+ con = (HttpURLConnection) url.openConnection();
+ con.setRequestMethod("GET");
+
+ status = con.getResponseCode();
+ in = new BufferedReader(
+ new InputStreamReader(con.getInputStream()));
+
+ content = new StringBuffer();
+ while ((inputLine = in.readLine()) != null) {
+ content.append(inputLine);
+ }
+ in.close();
+ con.disconnect();
+ System.out.println(status);
+ System.out.println(content);
+ if (!success) {
+ return false;
+ }
+ final long duration1 = interTime - startTime;
+ final long duration2 = interTime2 - interTime;
+ final long duration3 = System.nanoTime() - interTime2;
+ final long duration = System.nanoTime() - startTime;
+
+ System.out.print("N=");
+ System.out.println(numrecords);
+ System.out.print("CONNECT: ");
+ System.out.println(duration1/1000000);
+ System.out.print("SUBMIT: ");
+ System.out.println(duration2/1000000);
+ System.out.print("DELETE: ");
+ System.out.println(duration3/1000000);
+ System.out.println("");
+ System.out.print("TOTAL: ");
+ System.out.println(duration/1000000);
+
+ for (String k : usedIds.keySet()) {
+ Connection conn;
+ if (targetDb.matches(".*nsql3.*")) {
+ conn = DriverManager.getConnection(targetDb);
+ } else {
+ conn = DriverManager.getConnection(targetDb,"sa","Solna123");
+ }
+
+ Statement m_Statement = conn.createStatement();
+
+
+ String query = "SELECT [key] FROM "+targetTable+ " WHERE[key]='"+k+"'";
+
+ boolean inTessy = false;
+ ResultSet rs = m_Statement.executeQuery(query);
+ while (rs.next()) {
+ inTessy=true;
+ }
+ if (!inTessy) {
+ System.out.println("Submission of "+k+" failed.");
+ }
+ conn.close();
+ }
+
+ return true;
+ }
+
+ private static int writeXml(File xmlFile, File infile, String subj, ArrayList BNfields, ArrayList isolates, String targetDb, String targetTable) {
+ int numrecords = 0;
+ try {
+ Connection conn;
+ if (targetDb.matches(".*nsql3.*")) {
+ conn = DriverManager.getConnection(targetDb);
+ } else {
+ conn = DriverManager.getConnection(targetDb,"sa","Solna123");
+ }
+
+
+
+ BufferedReader br = new BufferedReader(new FileReader(infile));
+ ArrayList lines = new ArrayList();
+ HashMap indices = new HashMap();
+ String line = br.readLine();
+ String[] headFields = line.split("\t",-1);
+
+ if (headFields.length<2) {
+ System.out.println("Infile must be tab delimited");
+ br.close();
+ return 0;
+ }
+ for (int i = 0; i< headFields.length;i++) {
+ indices.put(headFields[i],i);
+ }
+
+
+
+
+ while ((line = br.readLine())!=null) {
+ lines.add(line);
+ }
+
+ br.close();
+
+
+
+ BufferedWriter bw = new BufferedWriter(new FileWriter(xmlFile));
+ PrintWriter pw = new PrintWriter(bw);
+
+ pw.println("");
+ pw.println("");
+ pw.println(" ");
+ pw.println("ECDC ");
+ pw.println(" ");
+ pw.println(" ");
+ pw.println(" ");
+ pw.println(" ");
+ pw.println(" ");
+ pw.println(" ");
+ pw.println("");
+ pw.println("");
+ pw.println(""+subj+" ");
+ pw.println(" ");
+ pw.println(""+subj+" ");
+ pw.println("UPDATE ");
+ pw.println(" ");
+ pw.println(" ");
+ pw.println("");
+
+ System.out.print("Lines read: ");
+ System.out.println(lines.size());
+
+ usedIds = new HashMap();
+
+ for (String l : lines) {
+
+ String[] fields = l.split("\t",-1);
+
+ if (isolates!=null && isolates.indexOf(fields[0])==-1) {
+ //System.out.println("Skipping "+guid);
+ continue;
+ }
+ String guid = fields[0].toUpperCase();
+ String oldGuid = guid;
+ if (!guid.startsWith("SEQ-")) {
+ guid = "SEQ-"+UUID.randomUUID().toString().toUpperCase();
+ }
+
+ //System.out.println(oldGuid);
+
+ String id = fields[indices.get("PB_RECORDID")];
+ if (id.equals("")) {
+
+ System.out.println("Entry "+oldGuid+ " is missing a RecordId");
+ continue;
+ }
+ String country = fields[indices.get("PB_REPORTINGCOUNTRY")];
+ if (country.equals("")) {
+
+ System.out.println("Entry "+oldGuid+ "("+id+") is missing a ReportingCountry");
+ country = "UNK";
+ }
+
+ Statement m_Statement = conn.createStatement();
+
+
+ String query = "SELECT [key] FROM "+targetTable+ " WHERE (PB_RECORDID='"+id+"' AND PB_REPORTINGCOUNTRY='"+country+"') OR [key]='"+oldGuid+"'";
+
+ boolean inTessy = false;
+ ResultSet rs = m_Statement.executeQuery(query);
+ while (rs.next()) {
+ String key = rs.getString(1);
+ guid = key;
+ //System.out.println(oldGuid+ " already exists in target database, using key: "+key);
+ inTessy = true;
+ }
+
+ String dateSampling = fields[indices.get("PB_DATEOFSAMPLING")];
+ String dateReflab = fields[indices.get("PB_DATEOFRECEIPTREFERENCELAB")];
+ String date = fields[indices.get("PB_DATEUSEDFORSTATISTICS")];
+
+ if (usedIds.containsKey(guid)) {
+ System.out.println(guid + " is duplicate! "+country+", "+id+", "+oldGuid);
+
+ continue;
+ } else {
+ usedIds.put(guid,1);
+
+ }
+
+ if (date.equals("")) {
+ if (!dateSampling.equals("")) {
+ date = dateSampling;
+ } else if (!dateReflab.equals("")) {
+ date = dateReflab;
+ } else {
+ String dateOrig = fields[indices.get("BN_ANN3")];
+ if (dateOrig==null) {
+ dateOrig = "";
+ }
+ if (dateOrig.matches("[0-9][0-9][0-9][0-9][-][0-9][0-9]-[0-9][0-9]")) {
+ date = dateOrig;
+ } else if (dateOrig.matches("[0-9][0-9][0-9][0-9][-][0-9][0-9]")) {
+ date = dateOrig+"-15";
+ } else if (dateOrig.matches("[0-9][0-9][0-9][0-9]")) {
+ date = dateOrig+"-07-01";
+ } else {
+ System.out.println("Entry "+oldGuid+"("+id+", "+country+") is missing Date and Ann3");
+ //continue;
+ date = "UNK";
+ }
+
+ }
+ fields[indices.get("PB_DATEUSEDFORSTATISTICS")] = date;
+ }
+
+
+ String enaId = "";
+ if (enaId.equals("") && !fields[indices.get("BN_WGS_ENAID")].equals("")) {
+ enaId = fields[indices.get("BN_WGS_ENAID")];
+ }
+ if (enaId.equals("") && !fields[indices.get("BN_WGS_SRAID")].equals("")) {
+ enaId = fields[indices.get("BN_WGS_SRAID")];
+ }
+
+
+
+
+ pw.println("");
+ pw.println(""+id+" ");
+ pw.println(""+country+" ");
+ pw.println("NEW/UPDATE ");
+ pw.println(""+date+" ");
+ pw.println(""+guid+" ");
+
+ for (int i = 1;i=fields.length) {
+ pw.close();
+ bw.close();
+ System.out.println("Entry "+guid+ " has too few columns");
+ return 0;
+ }
+ if (!headFields[i].startsWith("PB_") && !headFields[i].startsWith("BN_")) {
+ continue;
+ }
+ if (fields[i].equals("")) {
+ continue;
+ }
+ if (!enaId.equals("")) {
+ pw.println("");
+ pw.println("WGSENAID ");
+ pw.println(""+enaId+" ");
+ pw.println(" ");
+ }
+ if (headFields[i].toUpperCase().equals("BN_ANN3")) {
+ pw.println("");
+ pw.println("DATEOFRECEIPTREFERENCELABORIG ");
+ pw.println(""+fields[i]+" ");
+ pw.println(" ");
+ } else if (headFields[i].startsWith("PB_") && !inTessy) {
+ pw.println("");
+ pw.println(""+headFields[i].substring(3)+" ");
+ pw.println(""+fields[i]+" ");
+ pw.println(" ");
+ } else if (BNfields.indexOf(headFields[i].toUpperCase())!=-1) {
+ pw.println("");
+ pw.println(""+headFields[i]+" ");
+ pw.println(""+fields[i]+" ");
+ pw.println(" ");
+ }
+
+ }
+
+ pw.println(" ");
+ numrecords++;
+ if (numrecords>90) {
+ //break;
+ }
+ }
+
+
+
+ /*
+ for (int i = 0; i");
+ pw.println(""+id+" ");
+ pw.println("SE ");
+ pw.println("NEW/UPDATE ");
+ pw.println("2013-07-01 ");
+ pw.println(""+guid+" ");
+ pw.println("");
+ pw.println("Age ");
+ pw.println("53 ");
+ pw.println(" ");
+ pw.println("");
+ pw.println("Gender ");
+ pw.println("M ");
+ pw.println(" ");
+ pw.println("");
+ pw.println("DateOfSampling ");
+ pw.println("2013-07-01 ");
+ pw.println(" ");
+ pw.println("");
+ pw.println("DateOfReceiptReferenceLab ");
+ pw.println("2013-07-15 ");
+ pw.println(" ");
+ pw.println("");
+ pw.println("WgsProtocol ");
+ pw.println("MISEQ_2X150 ");
+ pw.println(" ");
+ pw.println("");
+ pw.println("WgsEnaId ");
+ pw.println("ERR1276254 ");
+ pw.println(" ");
+
+
+
+ pw.println("");
+
+
+ }
+ */
+
+ pw.println(" ");
+ pw.println(" ");
+ pw.println("");
+ pw.println(" ");
+
+
+
+ pw.close();
+ bw.close();
+ conn.close();
+ } catch (IOException | SQLException e) {
+
+ e.printStackTrace();
+ }
+ return numrecords;
+ }
+
+
+
+
+}
diff --git a/src/eu/europa/ecdc/enauploader/ImportConfig.java b/src/eu/europa/ecdc/enauploader/ImportConfig.java
new file mode 100644
index 0000000..cd9e593
--- /dev/null
+++ b/src/eu/europa/ecdc/enauploader/ImportConfig.java
@@ -0,0 +1,190 @@
+package eu.europa.ecdc.enauploader;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+
+// Class for holding the configuration for Data Import and scheduling
+// It contains fields and getters+setters for them
+public class ImportConfig implements Serializable {
+
+ private static final long serialVersionUID = -7632508112626524840L;
+ public static final int IMPORT_SQL = 0;
+ public static final int IMPORT_SQLITE = 1;
+ public static final int IMPORT_EXCEL = 2;
+ public static final int IMPORT_CSV = 3;
+
+ public static final int AUTH_NONE = 0;
+ public static final int AUTH_PASSWORD = 1;
+
+ public static final int SOURCE_STATIC = 0;
+ public static final int SOURCE_FLEXIBLE = 1;
+
+ public static final int SCHEDULE_NONE = 0;
+ public static final int SCHEDULE_MINUTE = 1;
+ public static final int SCHEDULE_HOUR = 2;
+ public static final int SCHEDULE_DAY = 3;
+
+ private int importType=0;
+ private int authType=0;
+ private int dataSourceFlexible=0;
+ private String databaseServer="";
+ private String database="";
+ private String datafile="";
+ private String datatable="";
+ private String sqlQuery="";
+ private LinkedHashMap map;
+ private LinkedHashMap constants;
+ private LinkedHashMap> valueMap;
+ private String username="";
+ private char[] password=new char[0];
+ private String subject="";
+ private ArrayList datasourceHeaders;
+
+ private int scheduleUnit=0;
+ private int scheduleAmount=0;
+
+ ImportConfig() {
+ setMap(new LinkedHashMap());
+ setConstants(new LinkedHashMap());
+ setValueMap(new LinkedHashMap>());
+ }
+
+ public int getImportType() {
+ return importType;
+ }
+
+ public void setImportType(int importType) {
+ this.importType = importType;
+ }
+
+ public int getAuthType() {
+ return authType;
+ }
+
+ public void setAuthType(int authType) {
+ this.authType = authType;
+ }
+
+ public int getDataSourceFlexible() {
+ return dataSourceFlexible;
+ }
+
+ public void setDataSourceFlexible(int dataSourceFlexible) {
+ this.dataSourceFlexible = dataSourceFlexible;
+ }
+
+ public String getSqlQuery() {
+ return sqlQuery;
+ }
+
+ public void setSqlQuery(String sqlQuery) {
+ this.sqlQuery = sqlQuery;
+ }
+
+ public LinkedHashMap getConstants() {
+ return constants;
+ }
+
+ public void setConstants(LinkedHashMap map) {
+ this.constants = map;
+ }
+
+ public LinkedHashMap getMap() {
+ return map;
+ }
+
+ public void setMap(LinkedHashMap map) {
+ this.map = map;
+ }
+
+ public String getUsername() {
+ return username;
+ }
+
+ public void setUsername(String username) {
+ this.username = username;
+ }
+
+ public char[] getPassword() {
+ return password;
+ }
+
+ public void setPassword(char[] cs) {
+ this.password = cs;
+ }
+
+ public String getSubject() {
+ return subject;
+ }
+
+ public void setSubject(String subject) {
+ this.subject = subject;
+ }
+
+ public String getDatabase() {
+ return database;
+ }
+
+ public void setDatabase(String database) {
+ this.database = database;
+ }
+
+ public String getDatafile() {
+ return datafile;
+ }
+
+ public void setDatafile(String datafile) {
+ this.datafile = datafile;
+ }
+
+ public String getDatatable() {
+ return datatable;
+ }
+
+ public void setDatatable(String datatable) {
+ this.datatable = datatable;
+ }
+
+ public String getDatabaseServer() {
+ return databaseServer;
+ }
+
+ public void setDatabaseServer(String databaseServer) {
+ this.databaseServer = databaseServer;
+ }
+
+ public LinkedHashMap> getValueMap() {
+ return valueMap;
+ }
+
+ public void setValueMap(LinkedHashMap> valueMap) {
+ this.valueMap = valueMap;
+ }
+
+ public int getScheduleUnit() {
+ return scheduleUnit;
+ }
+
+ public void setScheduleUnit(int scheduleUnit) {
+ this.scheduleUnit = scheduleUnit;
+ }
+
+ public int getScheduleAmount() {
+ return scheduleAmount;
+ }
+
+ public void setScheduleAmount(int scheduleAmount) {
+ this.scheduleAmount = scheduleAmount;
+ }
+
+ public ArrayList getDatasourceHeaders() {
+ return datasourceHeaders;
+ }
+
+ public void setDataSourceHeaders(ArrayList headers) {
+ datasourceHeaders = headers;
+ }
+
+
+}
diff --git a/src/eu/europa/ecdc/enauploader/ImportConfigGUI.java b/src/eu/europa/ecdc/enauploader/ImportConfigGUI.java
new file mode 100644
index 0000000..a40c081
--- /dev/null
+++ b/src/eu/europa/ecdc/enauploader/ImportConfigGUI.java
@@ -0,0 +1,937 @@
+package eu.europa.ecdc.enauploader;
+
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+import java.awt.FlowLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.image.BufferedImage;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+
+import javax.imageio.ImageIO;
+import javax.swing.BorderFactory;
+import javax.swing.JButton;
+import javax.swing.JComboBox;
+import javax.swing.JFileChooser;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JPasswordField;
+import javax.swing.JScrollPane;
+import javax.swing.JTabbedPane;
+import javax.swing.JTextArea;
+import javax.swing.JTextField;
+import javax.swing.UIManager;
+import javax.swing.UnsupportedLookAndFeelException;
+import javax.swing.filechooser.FileFilter;
+import javax.swing.filechooser.FileNameExtensionFilter;
+
+// This class creates a GUI for configuring Data import and scheduling
+public class ImportConfigGUI extends JFrame implements ActionListener {
+
+ private static final long serialVersionUID = -7815244026662021007L;
+ private HashMap configs;
+ private JButton newButton;
+ private JButton renameButton;
+ private JButton saveButton;
+ private JButton revertButton;
+ private JButton deleteButton;
+ private JTextArea descriptionArea;
+ private JComboBox confignameField;
+ private String oldSelection;
+ private boolean isMain;
+ private JComboBox configTypeBox;
+ private JComboBox authTypeBox;
+ private JComboBox flexibleSourceBox;
+ private JTextField infileField;
+ private JTextField indatabaseField;
+ private JTextField intableField;
+ private JTextField queryField;
+ private JTextField usernameField;
+ private JPasswordField passwordField;
+ private JComboBox subjectBox;
+
+ LinkedHashMap mapPanelHashMap;
+ private JPanel mapPanel;
+ private JTabbedPane tabs;
+ private JTextField databaseServerField;
+ private JComboBox scheduleBox;
+ private JTextField scheduleField;
+
+ private static final String UPLOAD_COLUMN = "Ready for upload";
+ private static final String BASE_FILE_COLUMN = "File base name";
+ private static final String ASSEMBLY_FILE_COLUMN = "Assembly file";
+ private static final String READ_FILES_COLUMN = "Read files";
+
+ // This can be run as main class, in that case the program will exit on GUI close
+ ImportConfigGUI(boolean isMain) {
+ this.isMain = isMain;
+ }
+
+ public void init() {
+
+ // Find all configs and read them
+ readConfigs();
+
+
+ // Init UI elements
+ try {
+ String imagePath = "media/ECDC2.png";
+ InputStream imgStream = ImportConfigGUI.class.getResourceAsStream(imagePath );
+ BufferedImage myImg;
+ myImg = ImageIO.read(imgStream);
+ setIconImage(myImg);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+
+ setTitle("ECDC WGS Uploader - Data import configuration");
+ this.setResizable(false);
+ JPanel mainPanel = new JPanel(new BorderLayout());
+ JPanel buttonPanel = new JPanel();
+ JPanel topPanel = new JPanel(new BorderLayout());
+
+ newButton = new JButton("New");
+ newButton.addActionListener(this);
+ newButton.setActionCommand("new");
+ renameButton = new JButton("Rename");
+ renameButton.addActionListener(this);
+ renameButton.setActionCommand("rename");
+ saveButton = new JButton("Save");
+ saveButton.addActionListener(this);
+ saveButton.setActionCommand("save");
+
+ revertButton = new JButton("Revert changes");
+ revertButton.addActionListener(this);
+ revertButton.setActionCommand("revert");
+ deleteButton = new JButton("Delete");
+ deleteButton.addActionListener(this);
+ deleteButton.setActionCommand("delete");
+
+ buttonPanel.add(newButton);
+ buttonPanel.add(renameButton);
+ buttonPanel.add(saveButton);
+
+ buttonPanel.add(revertButton);
+ buttonPanel.add(deleteButton);
+
+ JLabel descriptionLabel = new JLabel("Description:");
+ descriptionArea = new JTextArea();
+ descriptionArea.setPreferredSize(new Dimension(360, 80));
+ JPanel descriptionPanel = new JPanel();
+ descriptionPanel.add(descriptionLabel);
+ descriptionPanel.add(descriptionArea);
+ buttonPanel.add(descriptionPanel);
+
+
+ JLabel confignameLabel = new JLabel("Configuration");
+ confignameField = new JComboBox();
+ for (String k : configs.keySet()) {
+ confignameField.addItem(k);
+ }
+ confignameField.addActionListener(this);
+ confignameField.setActionCommand("selection");
+
+ confignameField.setPreferredSize(new Dimension(240,20));
+ JPanel confignamePanel = new JPanel ();
+ confignamePanel.add(confignameLabel);
+ confignamePanel.add(confignameField);
+
+
+ topPanel.add(buttonPanel,BorderLayout.CENTER);
+ topPanel.add(confignamePanel,BorderLayout.NORTH);
+ tabs = new JTabbedPane();
+ mainPanel.add(tabs,BorderLayout.CENTER);
+ mainPanel.add(topPanel,BorderLayout.NORTH);
+
+
+ JPanel cfgPanel = new JPanel();
+ FlowLayout mgr = new FlowLayout(FlowLayout.RIGHT);
+ cfgPanel.setLayout(mgr);
+ cfgPanel.setBorder(BorderFactory.createEtchedBorder());
+ populate(cfgPanel);
+ tabs.add("Import configuration", cfgPanel);
+
+ mapPanelHashMap = new LinkedHashMap();
+ mapPanel = new JPanel();
+ FlowLayout mgr2 = new FlowLayout(FlowLayout.RIGHT);
+ mapPanel.setLayout(mgr2);
+ mapPanel.setBorder(BorderFactory.createEtchedBorder());
+ String subject = (String) subjectBox.getSelectedItem();
+
+ JPanel loadHeadersPanel = new JPanel();
+ JButton loadHeadersButton = new JButton("Load column names from data source");
+ loadHeadersButton.addActionListener(this);
+ loadHeadersButton.setActionCommand("loadHeaders");
+ loadHeadersPanel.add(loadHeadersButton);
+ mapPanel.add(loadHeadersPanel);
+
+ JPanel constantPanel = new JPanel();
+ JLabel constantLabel = new JLabel("Fixed value Value mapping ");
+ constantPanel.add(constantLabel);
+ mapPanel.add(constantPanel);
+
+
+
+
+ tabs.add("Import variable mapping", mapPanel);
+
+ String item = (String)confignameField.getSelectedItem();
+ ImportConfig cfg = configs.get(item);
+ updateUi(cfg);
+ oldSelection = item;
+ updateMappingTab(subject,false,false);
+
+
+ /*
+ JScrollPane mainScroller = new JScrollPane(mainPanel);
+ mainScroller.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
+ mainScroller.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
+
+ JPanel mainPanel2 = new JPanel(new BorderLayout());
+ mainPanel2.add(mainScroller,BorderLayout.CENTER);
+ */
+
+ this.add(mainPanel);
+
+ if (isMain) {
+ this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+ }
+
+
+ this.setPreferredSize(new Dimension(640,1040));
+ this.pack();
+ this.setResizable(true);
+ this.setVisible(true);
+ }
+
+ public ImportConfig getActiveConfig() {
+ String item = (String)confignameField.getSelectedItem();
+ ImportConfig cfg = configs.get(item);
+ return cfg;
+ }
+
+ private void readConfigs() {
+ // Find all configs and read them
+ configs = new HashMap();
+
+ //TODO: Find a suitable location for the CONFIG_DIR constant and make it available to the classes that needs it
+ File dir = new File(".");
+ File[] files = dir.listFiles();
+ for (File f : files) {
+ if (f.getName().endsWith(".import")) {
+ try {
+ ImportConfig cfg = ImportConfigHandler.loadConfig(f);
+ configs.put(f.getName(),cfg);
+ } catch (ClassNotFoundException | IOException e) {
+ e.printStackTrace();
+ JOptionPane.showMessageDialog(null,
+ "Could not open config file "+f.toString()+". If it is open in another program, please close it and try again.");
+ }
+ }
+ }
+
+ }
+
+ // This methods updates the GUI components from an ImportConfig object
+ private void updateUi(ImportConfig cfg) {
+
+ if (cfg==null) {
+ return;
+ }
+
+ configTypeBox.setSelectedIndex(cfg.getImportType());
+ authTypeBox.setSelectedIndex(cfg.getAuthType());
+ flexibleSourceBox.setSelectedIndex(cfg.getDataSourceFlexible());
+
+ infileField.setText(cfg.getDatafile());
+ databaseServerField.setText(cfg.getDatabaseServer());
+ indatabaseField.setText(cfg.getDatabase());
+ intableField.setText(cfg.getDatatable());
+ queryField.setText(cfg.getSqlQuery());
+ usernameField.setText(cfg.getUsername());
+ passwordField.setText(new String(cfg.getPassword()));
+ subjectBox.setSelectedItem(cfg.getSubject());
+
+ scheduleBox.setSelectedIndex(cfg.getScheduleUnit());
+ scheduleField.setText(Integer.toString(cfg.getScheduleAmount()));
+
+ updateMappingTab(cfg.getSubject(),true,false);
+
+ LinkedHashMap dataMap = cfg.getMap();
+ for (String k : dataMap.keySet()) {
+ String value = dataMap.get(k);
+ mapPanelHashMap.get(k).setMappingField(value);
+ }
+
+ LinkedHashMap constants = cfg.getConstants();
+ for (String k : constants.keySet()) {
+ String value = constants.get(k);
+ mapPanelHashMap.get(k).setConstant(value);
+ }
+
+ }
+
+
+ // Populate the first panel, for import configuration.
+ private void populate(JPanel cfgPanel) {
+
+ JPanel subjectPanel = new JPanel();
+ JLabel subjectLabel = new JLabel("Subject for the import: ");
+ subjectBox = new JComboBox();
+ File dir = new File(".");
+ File[] files = dir.listFiles();
+
+ // Check which subjects that are available and add them to the dropdown
+ for (File f : files) {
+ if (f.getName().endsWith(".meta")) {
+ String name = f.getName().substring(0,f.getName().length()-5);
+ subjectBox.addItem(name);
+ }
+ }
+
+ subjectPanel.add(subjectLabel);
+ subjectPanel.add(subjectBox);
+ cfgPanel.add(subjectPanel);
+
+ JPanel configTypePanel = new JPanel();
+ JLabel configTypeLabel = new JLabel("Type of data source for import: ");
+ String[] options = {"SQL","SQLite","Excel","csv"};
+ configTypeBox = new JComboBox(options);
+ configTypePanel.add(configTypeLabel);
+ configTypePanel.add(configTypeBox);
+ cfgPanel.add(configTypePanel);
+
+ JPanel authTypePanel = new JPanel();
+ JLabel authTypeLabel = new JLabel("Authentication method (for SQL or SQLite): ");
+ String[] options2 = {"None/Integrated","Password"};
+ authTypeBox = new JComboBox(options2);
+ authTypePanel.add(authTypeLabel);
+ authTypePanel.add(authTypeBox);
+ cfgPanel.add(authTypePanel);
+
+ JPanel flexibleSourcePanel = new JPanel();
+ JLabel flexibleSourceLabel = new JLabel("Choose data source (file/database+table) every time the import is run? ");
+ String[] options3 = {"No","Yes"};
+ flexibleSourceBox = new JComboBox(options3);
+ flexibleSourcePanel.add(flexibleSourceLabel);
+ flexibleSourcePanel.add(flexibleSourceBox);
+ cfgPanel.add(flexibleSourcePanel);
+
+ JPanel infilePanel = new JPanel();
+ JLabel infileLabel = new JLabel("File for CSV/Excel/SQLite import ");
+ infileField = new JTextField("");
+ infileField.setPreferredSize(new Dimension(240, 22));
+ JButton chooseInfileButton = new JButton("Browse");
+ chooseInfileButton.setActionCommand("browseFile");
+ chooseInfileButton.addActionListener(this);
+ infilePanel.add(infileLabel);
+ infilePanel.add(infileField);
+ infilePanel.add(chooseInfileButton);
+ cfgPanel.add(infilePanel);
+
+ // For SQL databases, the apropriate drivers must be available. Right now MySQL and Sqlserver drivers are included.
+ String serverToolTipText = "Must be prefixed with protocol:// and end with :port For example: mysql://localhost:1433 Supported protocols: sqlserver mysql";
+ JPanel databaseServerPanel = new JPanel();
+ JLabel databaseServerLabel = new JLabel("Database server for SQL import ");
+ databaseServerField = new JTextField("");
+ databaseServerField.setPreferredSize(new Dimension(240, 22));
+ databaseServerField.setToolTipText(serverToolTipText);
+ databaseServerPanel.add(databaseServerLabel);
+ databaseServerPanel.add(databaseServerField);
+ databaseServerPanel.setToolTipText(serverToolTipText);
+ cfgPanel.add(databaseServerPanel);
+
+ JPanel indatabasePanel = new JPanel();
+ JLabel indatabaseLabel = new JLabel("Database for SQL import ");
+ indatabaseField = new JTextField("");
+ indatabaseField.setPreferredSize(new Dimension(240, 22));
+ indatabasePanel.add(indatabaseLabel);
+ indatabasePanel.add(indatabaseField);
+ cfgPanel.add(indatabasePanel);
+
+
+ // TODO: Dynamically read sheet name from Excel
+ JPanel intablePanel = new JPanel();
+ JLabel intableLabel = new JLabel("Table/view for SQL import, sheet name for Excel import ");
+ intableField = new JTextField("");
+ intableField.setPreferredSize(new Dimension(140, 22));
+ intablePanel.add(intableLabel);
+ intablePanel.add(intableField);
+ cfgPanel.add(intablePanel);
+
+ JPanel queryPanel = new JPanel();
+ JLabel queryLabel = new JLabel("Filter to be used with SQL (using WHERE clause) ");
+ queryField = new JTextField("");
+ queryField.setPreferredSize(new Dimension(200, 22));
+ queryPanel.add(queryLabel);
+ queryPanel.add(queryField);
+ cfgPanel.add(queryPanel);
+
+ JPanel usernamePanel = new JPanel();
+ JLabel usernameLabel = new JLabel("Username (if authentication is used) ");
+ usernameField = new JTextField("");
+ usernameField.setPreferredSize(new Dimension(200, 22));
+ usernamePanel.add(usernameLabel);
+ usernamePanel.add(usernameField);
+ cfgPanel.add(usernamePanel);
+
+ JPanel passwordPanel = new JPanel();
+ JLabel passwordLabel = new JLabel("Password (if authentication is used) ");
+ passwordField = new JPasswordField();
+ passwordField.setPreferredSize(new Dimension(200, 22));
+ passwordPanel.add(passwordLabel);
+ passwordPanel.add(passwordField);
+ cfgPanel.add(passwordPanel);
+
+ JPanel schedulePanel = new JPanel();
+ JLabel scheduleLabel = new JLabel("Import, link, and submit to configured systems every");
+ scheduleField = new JTextField("");
+ scheduleField.setPreferredSize(new Dimension(80, 22));
+ String[] options4 = {"- (Not enabled)","Minutes","Hours","Days"};
+ scheduleBox = new JComboBox(options4);
+ schedulePanel.add(scheduleLabel);
+ schedulePanel.add(scheduleField);
+ schedulePanel.add(scheduleBox);
+ cfgPanel.add(schedulePanel);
+ }
+
+ // Actions
+ @Override
+ public void actionPerformed(ActionEvent e) {
+
+ String command = e.getActionCommand();
+
+ // Close button, closes the GUI
+ if (command.equals("close")) {
+ this.dispose();
+
+ // Load the headers from the configured data source into the mappings tab
+ } else if (command.equals("loadHeaders")) {
+ int tab = tabs.getSelectedIndex();
+ if (tab==1) {
+ String subject = (String) subjectBox.getSelectedItem();
+ updateMappingTab(subject,true,true);
+ }
+
+ // Save configs
+ } else if (command.equals("save")) {
+
+ ImportConfig cfg = createConfig();
+ String selected = (String)confignameField.getSelectedItem();
+ if (selected!=null) {
+ configs.put(selected,cfg);
+ }
+
+ for (String k : configs.keySet()) {
+ ImportConfig cfg2 = configs.get(k);
+ File f = new File(k);
+ if (cfg2!=null && f!=null) {
+ try {
+ ImportConfigHandler.saveConfig(f, cfg2);
+ } catch (IOException e1) {
+ e1.printStackTrace();
+ JOptionPane.showMessageDialog(null,
+ "Could not save to config file "+f.toString()+". If it is open in another program, please close it and try again.");
+ }
+ }
+ }
+
+ // Revert to saved state
+ } else if (command.equals("revert")) {
+ for (String name : configs.keySet()) {
+ File f = new File(name);
+ if (f.exists()) {
+ try {
+ ImportConfig cfg = ImportConfigHandler.loadConfig(f);
+ configs.put(name,cfg);
+ } catch (ClassNotFoundException | IOException e1) {
+
+ e1.printStackTrace();
+ }
+ } else {
+ configs.put(name,new ImportConfig());
+ }
+ }
+ ImportConfig cfg = configs.get((String)confignameField.getSelectedItem());
+ updateUi(cfg);
+
+ // Browse for a file to import from
+ } else if (command.equals("browseFile")) {
+ JFileChooser chooser = new JFileChooser();
+ chooser.setCurrentDirectory(new java.io.File("."));
+
+ if (configTypeBox.getSelectedItem().equals("csv")) {
+ FileFilter filterCsv = new FileNameExtensionFilter("CSV file (.csv)","csv");
+ chooser.addChoosableFileFilter(filterCsv);
+ } else if (configTypeBox.getSelectedItem().equals("Excel")) {
+ FileFilter filterExcel = new FileNameExtensionFilter("Excel file (.xlsx)","xlsx");
+ chooser.addChoosableFileFilter(filterExcel);
+ } else if (configTypeBox.getSelectedItem().equals("SQLite")) {
+ FileFilter filterExcel = new FileNameExtensionFilter("SQLite database (.db, .sqlite)","db","sqlite");
+ chooser.addChoosableFileFilter(filterExcel);
+ } else {
+ return;
+ }
+
+ chooser.setAcceptAllFileFilterUsed(true);
+ chooser.setDialogTitle("Choose file for import");
+ chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
+ chooser.setAcceptAllFileFilterUsed(true);
+
+ if (chooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
+ infileField.setText(chooser.getSelectedFile().toString());
+ }
+
+ // If another config is selected, update GUI to reflect the data in this config
+ } else if (command.equals("selection")) {
+
+ // Put data for previously selected config into an object
+ ImportConfig cfgOld = createConfig();
+ if (oldSelection!=null) {
+ configs.put(oldSelection,cfgOld);
+ }
+
+ String item = (String)confignameField.getSelectedItem();
+ ImportConfig cfg = configs.get(item);
+ updateUi(cfg);
+
+ // Make the newly selected config the one to save to an object on next selection action
+ oldSelection = item;
+
+ // Create new config
+ } else if (command.equals("new")) {
+
+ // Get user input for config name
+ String s = (String)JOptionPane.showInputDialog(
+ this,
+ "Choose a name for the config:",
+ "Create new config",
+ JOptionPane.PLAIN_MESSAGE
+ );
+
+ if ((s != null) && (s.length() > 0)) {
+ if (!s.endsWith(".import")) {
+ s = s + ".import";
+ }
+ for (String k : configs.keySet()) {
+ if (s.equals(k)) {
+ JOptionPane.showMessageDialog(this,
+ "This config name already exists.");
+ return;
+ }
+ }
+
+ // Create config and update GUI
+ ImportConfig cfg = new ImportConfig();
+ configs.put(s,cfg);
+ confignameField.addItem(s);
+ confignameField.setSelectedItem(s);
+ updateUi(cfg);
+ }
+
+
+ // Rename config
+ } else if (command.equals("rename")) {
+
+ // Get old name
+ String name = (String)confignameField.getSelectedItem();
+ if (name==null) {
+ return;
+ }
+
+ // Get user input for new name
+ String s = (String)JOptionPane.showInputDialog(
+ this,
+ "Choose a new name for config "+name,
+ "Rename config",
+ JOptionPane.PLAIN_MESSAGE
+ );
+
+ if ((s != null) && (s.length() > 0)) {
+ if (!s.endsWith(".import")) {
+ s = s + ".import";
+ }
+ for (String k : configs.keySet()) {
+ if (s.equals(k)) {
+ JOptionPane.showMessageDialog(this,
+ "This config name already exists, choose another name.");
+ return;
+ }
+ }
+
+ // Create new config from current data shown in GUI
+ ImportConfig cfg = createConfig();
+
+ // If config is saved to file, save to new file and get rid of the old one
+ // TODO: Potential issue, if the new file is created and the delete then fails, there is a duplication.
+ File oldFile = new File(name);
+ File newFile = new File(s);
+ if (oldFile.exists()) {
+ try {
+ ImportConfigHandler.saveConfig(newFile, cfg);
+ oldFile.delete();
+ } catch (IOException e1) {
+ e1.printStackTrace();
+ JOptionPane.showMessageDialog(this,
+ "Writing to file "+s+" failed. Aborting rename.");
+ return;
+ }
+ }
+
+ // Replace config in HashMap
+ configs.remove(name);
+ configs.put(s,cfg);
+
+ // Reflect the name change in the GUI
+ confignameField.removeItem(name);
+ confignameField.addItem(s);
+ confignameField.setSelectedItem(s);
+ updateUi(cfg);
+ }
+
+
+ // Delete config (This is only a logical deletion, file will be renamed to .import.bak and will not be visible any more)
+ // It can be restored through the file system by removing the .bak
+ } else if (command.equals("delete")) {
+ String name = (String)confignameField.getSelectedItem();
+ if (name==null) {
+ return;
+ }
+
+ // Ask user to confirm
+ int n = JOptionPane.showConfirmDialog(
+ this,
+ "Are you sure? This will rename the config file to "+name+".bak\n and it will no longer be visible.",
+ "Delete config?",
+ JOptionPane.YES_NO_OPTION);
+ if (n==JOptionPane.YES_OPTION) {
+ // TODO: This is replication of code with the rename command, can be extracted to method
+ File oldFile = new File(name);
+ File newFile = new File(name+".bak");
+ ImportConfig cfg = createConfig();
+ if (oldFile.exists()) {
+ try {
+ ImportConfigHandler.saveConfig(newFile, cfg);
+ oldFile.delete();
+ } catch (IOException e1) {
+ e1.printStackTrace();
+ JOptionPane.showMessageDialog(this,
+ "Deletion failed.");
+ return;
+
+ }
+ }
+
+
+ oldSelection = null;
+ confignameField.removeItem(name);
+ try {
+ // Try to set the selected config to the first item in the dropdown
+ confignameField.setSelectedIndex(0);
+ ImportConfig cfg2 = configs.get((String)confignameField.getSelectedItem());
+ updateUi(cfg2);
+ String item = (String)confignameField.getSelectedItem();
+ oldSelection = item;
+ } catch (Exception e2) {
+ // If no other configs exist, generate an empty one
+ ImportConfig cfg2 = new ImportConfig();
+ updateUi(cfg2);
+ }
+
+ }
+
+ }
+ }
+
+ // This method updates the mapping tab with the headings from the selected subject metadata file
+ // and the headings from the selected data source (if it is valid)
+ private void updateMappingTab(String subject, boolean forceUpdate, boolean readHeadersFromSource) {
+
+ //Get the selected config
+ ImportConfig cfg = configs.get(confignameField.getSelectedItem());
+
+ //TODO: This base directory should be in global field CONFIG_DIR
+ File dir = new File(".");
+ File[] files = dir.listFiles();
+ for (File f : files) {
+ // Find the corresponding subject metadata file
+ if (f.getName().equals(subject+".meta")) {
+
+ // Try to read all headers from data source, just add empty if data source returned null header
+ ArrayList columns = new ArrayList();
+ columns.add("");
+
+ ArrayList header=null;
+
+ if (readHeadersFromSource) {
+ header = readDataHeader();
+ ImportConfig activeCfg = getActiveConfig();
+ if (activeCfg!=null) {
+ activeCfg.setDataSourceHeaders(header);
+ }
+ } else {
+ ImportConfig activeCfg = getActiveConfig();
+ if (activeCfg!=null) {
+ header = activeCfg.getDatasourceHeaders();
+ }
+ if (header==null) {
+ header = readDataHeader();
+ if (activeCfg!=null) {
+ activeCfg.setDataSourceHeaders(header);
+ }
+ }
+ }
+
+ if (header!=null) {
+ columns.addAll(header);
+ }
+
+ // Open metadata file and step through the fields, adding a controlled vocabulary when provided
+ // TODO: This is a bit hacky, it entagles GUI operations with file parsing, should be rewritten
+ ArrayList fields = new ArrayList();
+ ArrayList lines = new ArrayList();
+ BufferedReader br;
+ try {
+
+ // Read all lines from metadata file
+ br = new BufferedReader(new FileReader(f));
+ String line;
+ while ((line=br.readLine())!=null) {
+ lines.add(line);
+ }
+ br.close();
+
+ lines.add(UPLOAD_COLUMN+"=Yes,No");
+ lines.add(BASE_FILE_COLUMN);
+ lines.add(ASSEMBLY_FILE_COLUMN);
+ lines.add(READ_FILES_COLUMN);
+
+ // Iterate over the fields
+ for (String field : lines) {
+
+ // Check if there is a '=' present, if so, the field name is everything up to it
+ int dividerIndex = field.indexOf("=");
+ String[] vocabulary=null;
+ if (dividerIndex!=-1) {
+ vocabulary = field.substring(dividerIndex+1).split(",",-1);
+ field = field.substring(0, dividerIndex);
+ }
+ fields.add(field);
+
+ // Check if this is a field not previously in the GUI, or if the method was called with a force update flag
+ if (!mapPanelHashMap.containsKey(field) || forceUpdate) {
+
+ // Generate a new panel, or get an existing one
+ ImportMapPanel panel;
+ if (mapPanelHashMap.containsKey(field)) {
+ // panel already exists, update
+ panel = mapPanelHashMap.get(field);
+ panel.setMappingFields(columns);
+
+ } else {
+ // New panel, generate it
+
+ // Get map between import values and metadata values if there is a controlled vocabulary
+ LinkedHashMap valueMap = null;
+ if (cfg!=null) {
+ valueMap = cfg.getValueMap().get(field);
+ }
+ panel = new ImportMapPanel(field,columns,vocabulary, valueMap);
+ }
+
+ // Set the mapping for this panel
+ if (cfg!=null) {
+ String value = cfg.getMap().get(field);
+ if (value!=null) {
+ panel.setMappingField(value);
+ }
+ }
+
+ // Add panel to HashMap and to GUI
+ mapPanelHashMap.put(field,panel);
+ mapPanel.add(panel);
+ }
+ }
+
+ // Iterate over all existing panels, if it is not in the metadata set, remove it from GUI and HashMap
+ for (String k : new LinkedHashSet(mapPanelHashMap.keySet())) {
+ if (fields.indexOf(k)==-1) {
+ mapPanel.remove(mapPanelHashMap.get(k));
+ mapPanelHashMap.remove(k);
+ }
+ }
+
+ } catch (IOException e) {
+ e.printStackTrace();
+ continue;
+
+ }
+ }
+ }
+ }
+
+ // Read the header from the import data source
+ private ArrayList readDataHeader() {
+
+ System.out.println("Reading headers from source");
+ ArrayList header=new ArrayList();
+
+ // Get parameters from GUI
+ File infile = new File (infileField.getText());
+ String database = indatabaseField.getText();
+ String databaseServer = databaseServerField.getText();
+ String table = intableField.getText();
+ int dataType = configTypeBox.getSelectedIndex();
+
+
+ switch (dataType) {
+
+ // CSV
+ case ImportConfig.IMPORT_CSV:
+ if (!infile.exists()) {
+ error("File does not exist");
+ return header;
+ }
+ header = ImportTools.readCsvHeader(infile);
+ break;
+
+ //Excel
+ case ImportConfig.IMPORT_EXCEL:
+ if (!infile.exists()) {
+ error("File does not exist");
+ return header;
+ }
+ if (table.equals("")) {
+ error("Excel sheet must be chosen");
+ return header;
+ }
+ header = ImportTools.readExcelHeader(infile, table);
+ break;
+
+ //SQLite
+ case ImportConfig.IMPORT_SQLITE:
+ if (!infile.exists()) {
+ error("File does not exist");
+ return header;
+ }
+
+ if (table.equals("")) {
+ error("SQLite table must be chosen");
+ return header;
+ }
+ header = ImportTools.readSqliteHeader(infile.toString(), table);
+ break;
+
+ // SQL
+ case ImportConfig.IMPORT_SQL:
+ if (databaseServer.equals("")) {
+ return header;
+ }
+ if (database.equals("")) {
+ error("SQL database must be chosen");
+ return header;
+ }
+ if (table.equals("")) {
+ error("SQL table must be chosen");
+ return header;
+ }
+ int authMethod = authTypeBox.getSelectedIndex();
+ String username = usernameField.getText();
+ char[] password = passwordField.getPassword();
+ ImportSqlAuth auth = new ImportSqlAuth(authMethod, username, password);
+ header = ImportTools.readSqlHeader(databaseServer, database, table, auth);
+ break;
+
+ }
+
+
+ return header;
+ }
+
+ private void error(String message) {
+ JOptionPane.showMessageDialog(this, message);
+ }
+
+ // Generate an ImportConfig object from the current GUI data
+ private ImportConfig createConfig() {
+
+ ImportConfig activeConfig = getActiveConfig();
+ ArrayList datasourceHeaders = activeConfig.getDatasourceHeaders();
+ ImportConfig cfg = new ImportConfig();
+ cfg.setDataSourceHeaders(datasourceHeaders);
+ cfg.setImportType(configTypeBox.getSelectedIndex());
+ cfg.setAuthType(authTypeBox.getSelectedIndex());
+ cfg.setDataSourceFlexible(flexibleSourceBox.getSelectedIndex());
+ cfg.setDatafile(infileField.getText());
+ cfg.setDatabaseServer(databaseServerField.getText());
+ cfg.setDatabase(indatabaseField.getText());
+ cfg.setDatatable(intableField.getText());
+ cfg.setSqlQuery(queryField.getText());
+ cfg.setUsername(usernameField.getText());
+ cfg.setPassword(passwordField.getPassword());
+ cfg.setSubject((String)subjectBox.getSelectedItem());
+
+ int scheduleAmount = 0;
+ try {
+ scheduleAmount = Integer.parseInt(scheduleField.getText());
+ } catch (Exception e) {
+ scheduleAmount = 0;
+ }
+ cfg.setScheduleAmount(scheduleAmount);
+ cfg.setScheduleUnit(scheduleBox.getSelectedIndex());
+
+ LinkedHashMap> valueMapMap = new LinkedHashMap>();
+ LinkedHashMap dataMap = new LinkedHashMap();
+ for (String k : mapPanelHashMap.keySet()) {
+ dataMap.put(k, mapPanelHashMap.get(k).getMappingFieldname());
+ valueMapMap.put(k, mapPanelHashMap.get(k).getValueMap());
+ }
+ cfg.setMap(dataMap);
+ cfg.setValueMap(valueMapMap);
+
+ LinkedHashMap constants = new LinkedHashMap();
+ for (String k : mapPanelHashMap.keySet()) {
+ constants.put(k, mapPanelHashMap.get(k).getConstant());
+ }
+ cfg.setConstants(constants);
+
+ return cfg;
+ }
+
+ // Main method for running this GUI stand alone
+ public static void main(String[] args) {
+ try {
+ // Set System L&F
+ UIManager.setLookAndFeel(
+ UIManager.getSystemLookAndFeelClassName());
+ }
+ catch (UnsupportedLookAndFeelException e) {
+
+ }
+ catch (ClassNotFoundException e) {
+
+ }
+ catch (InstantiationException e) {
+
+ }
+ catch (IllegalAccessException e) {
+
+ }
+
+ ImportConfigGUI gui = new ImportConfigGUI(true);
+ gui.init();
+ }
+
+
+}
diff --git a/src/eu/europa/ecdc/enauploader/ImportConfigHandler.java b/src/eu/europa/ecdc/enauploader/ImportConfigHandler.java
new file mode 100644
index 0000000..bb9a932
--- /dev/null
+++ b/src/eu/europa/ecdc/enauploader/ImportConfigHandler.java
@@ -0,0 +1,29 @@
+package eu.europa.ecdc.enauploader;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+
+// Read and write routine for ImportConfig files.
+// These can be replaced if another file format than serialized Java objects is to be implemented
+
+public class ImportConfigHandler {
+
+ public static ImportConfig loadConfig(File configFile) throws IOException, ClassNotFoundException {
+ FileInputStream streamIn = new FileInputStream(configFile);
+ ObjectInputStream objectinputstream = new ObjectInputStream(streamIn);
+ ImportConfig config = (ImportConfig) objectinputstream.readObject();
+ objectinputstream.close();
+ return config;
+ }
+
+ public static void saveConfig(File configFile, ImportConfig config) throws IOException {
+ FileOutputStream fout = new FileOutputStream(configFile);
+ ObjectOutputStream oos = new ObjectOutputStream(fout);
+ oos.writeObject(config);
+ oos.close();
+ }
+}
diff --git a/src/eu/europa/ecdc/enauploader/ImportMapPanel.java b/src/eu/europa/ecdc/enauploader/ImportMapPanel.java
new file mode 100644
index 0000000..6abe4f4
--- /dev/null
+++ b/src/eu/europa/ecdc/enauploader/ImportMapPanel.java
@@ -0,0 +1,213 @@
+package eu.europa.ecdc.enauploader;
+
+import java.awt.Dimension;
+import java.awt.FlowLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+
+import javax.swing.JButton;
+import javax.swing.JComboBox;
+import javax.swing.JDialog;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTable;
+import javax.swing.JTextField;
+import javax.swing.table.DefaultTableModel;
+
+// This class implements a panel for the mapping of a single data field
+public class ImportMapPanel extends JPanel implements ActionListener {
+
+ private static final long serialVersionUID = 6385671140689063455L;
+ private String name;
+ private JComboBox mappingFieldnameBox;
+ private String[] vocabulary;
+ private JTextField constantField;
+ private JComboBox constantBox;
+ boolean multiChoice;
+ private LinkedHashMap valueMap;
+
+
+ ImportMapPanel(String name, ArrayList options,String[] vocabulary, LinkedHashMap valueMap) {
+ this.name = name;
+ this.vocabulary = vocabulary;
+
+ // If the mapping of values between controlled vocabularies is empty, generate a default one
+ if (valueMap==null && vocabulary != null) {
+ valueMap = new LinkedHashMap();
+ for (String v : vocabulary) {
+ valueMap.put(v,v);
+ }
+ }
+
+ this.valueMap = valueMap;
+
+ // Init GUI components
+ this.setLayout(new FlowLayout(FlowLayout.RIGHT));
+ mappingFieldnameBox = new JComboBox();
+ setMappingFields(options);
+
+ JLabel label = new JLabel(this.name);
+
+ // For free text, use a JtextField, for controlled vocabularies, use a JComboBox for the constant field
+ if (vocabulary==null || vocabulary.length==0) {
+ constantField = new JTextField("");
+ constantField.setPreferredSize(new Dimension(80,25));
+ multiChoice=false;
+ } else {
+ constantBox = new JComboBox();
+ constantBox.addItem("");
+ for (String c : this.vocabulary) {
+ constantBox.addItem(c);
+ }
+ constantBox.setPreferredSize(new Dimension(80,25));
+ multiChoice=true;
+ }
+
+
+ this.add(label);
+ this.add(mappingFieldnameBox);
+ if (multiChoice) {
+ this.add(constantBox);
+ } else {
+ this.add(constantField);
+ }
+
+ // If there is a controlled vocabulary, enable the button for mapping to it
+ JButton valueMappingButton = new JButton("Map values");
+ if (vocabulary==null || vocabulary.length==0) {
+ valueMappingButton.setEnabled(false);
+ } else {
+ valueMappingButton.setEnabled(true);
+ }
+ valueMappingButton.setActionCommand("map");
+ valueMappingButton.addActionListener(this);
+
+ this.add(valueMappingButton);
+
+ this.setPreferredSize(new Dimension(600,30));
+ }
+
+ // Get the mapping field name
+ public String getMappingFieldname() {
+ return (String) mappingFieldnameBox.getSelectedItem();
+ }
+
+ // Get the value map
+ public LinkedHashMap getValueMap() {
+ return valueMap;
+ }
+
+ // Get the constant field value
+ public String getConstant() {
+ if (multiChoice) {
+ return (String)constantBox.getSelectedItem();
+ } else {
+ return constantField.getText();
+ }
+ }
+
+ // Get the field name (from metadata file)
+ public String getName() {
+ return name;
+ }
+
+ // Set the possible fields to map from
+ public void setMappingFields(ArrayList columns) {
+ mappingFieldnameBox.removeAllItems();
+ for (String option : columns) {
+ mappingFieldnameBox.addItem(option);
+ }
+ repaint();
+ }
+
+
+ // Set the choice for field to map from
+ public void setMappingField(String value) {
+ mappingFieldnameBox.setSelectedItem(value);
+ }
+
+ // Set the constant value
+ public void setConstant(String value) {
+ if (multiChoice) {
+ constantBox.setSelectedItem(value);
+ } else {
+ constantField.setText(value);
+ }
+
+ }
+
+
+ // Handler for event for the value mapping button, it opens a dialog with a table of mappings
+ // TODO: Make this nicer looking and add JComboBox for controlled vocabulary
+ @Override
+ public void actionPerformed(ActionEvent arg0) {
+
+ // Generate data for mapping table
+ String[] headers = {"Local value","TESSy value"};
+ String[][] data = new String[valueMap.size()][headers.length];
+ int c = 0;
+ for (String k : valueMap.keySet()) {
+ String[] row = new String[headers.length];
+ row[0] = k;
+ row[1] = valueMap.get(k);
+ data[c] = row;
+ c++;
+ }
+
+ // Create table
+ DefaultTableModel model = new DefaultTableModel(data,headers);
+ JTable table = new JTable(model);
+ JScrollPane scroller = new JScrollPane(table);
+ JPanel panel = new JPanel();
+ panel.add(scroller);
+
+ // Button for a dding a new rom to the mapping
+ JButton addButton = new JButton("Add row");
+ addButton.setActionCommand("add");
+ addButton.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ model.addRow(new String[2]);
+
+ }
+ });
+ panel.add(addButton);
+
+ // Button for removing a row from the mapping
+ JButton removeButton = new JButton("Remove row");
+ removeButton.setActionCommand("remove");
+ removeButton.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ model.removeRow(table.getSelectedRow());
+ }
+ });
+ panel.add(removeButton);
+
+ //Button for saving the value map
+ JButton saveButton = new JButton("Save");
+ saveButton.setActionCommand("save");
+ saveButton.addActionListener(new ActionListener() {
+
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ for (int i = 0 ;i nextActions;
+ private HashMap importSubjects;
+ private HashMap intervals;
+
+ private SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+
+
+ // Constructor, ties the scheduler to a GUI
+ ImportScheduler(EcdcUploaderGUI gui) {
+ super(gui, null, null, null, null, null);
+
+ intervals = new HashMap();
+ nextActions = new HashMap();
+ importSubjects = new HashMap();
+ }
+
+ // Go through all the import configs and find any requests for scheduling
+ // Only update if they have changed, to not interrupt running schedules
+ public int setImportConfigHash(HashMap importConfigHash) {
+
+ log("Loading scheduled imports and submissions...");
+ int schedules = 0;
+ for (String configName : importConfigHash.keySet()) {
+ ImportConfig cfg = importConfigHash.get(configName);
+
+ // Calculate interval in seconds
+ long interval = cfg.getScheduleAmount() * unitAmounts[cfg.getScheduleUnit()];
+ Long oldInterval = intervals.get(configName);
+ if (oldInterval != null && interval == oldInterval) {
+ //Interval is unchanged, do not update
+ continue;
+ }
+
+ // If interval is negative, no scheduling has been configured. Get rid of any existing schedule for this config
+ if (interval<=0) {
+ intervals.put(configName, null);
+ nextActions.put(configName, null);
+ importSubjects.put(configName,null);
+
+ // If scheduling, calculate the time for next import/upload
+ } else {
+ Date nextAction = new Date( System.currentTimeMillis() + interval * 1000);
+ intervals.put(configName, interval);
+ nextActions.put(configName, nextAction);
+ importSubjects.put(configName,cfg.getSubject());
+ schedules++;
+ SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+ log("Scheduled "+configName+" for "+df.format(nextAction));
+ }
+ }
+ log("Done loading. "+Integer.toString(schedules)+" different configurations scheduled.");
+ return schedules;
+ }
+
+ // Main method for this worker, it waits until it is time to import/upload and then does so
+ @Override
+ protected Object doInBackground() throws Exception {
+
+ setTitle("Import and submission scheduler");
+ setStatus("Waiting for scheduled jobs");
+ setProgress(5);
+
+ // Run forever
+ while (true) {
+
+ Date now = new Date();
+
+ // Iterate through nextActions HashMap
+ for (String configName : nextActions.keySet()) {
+ setStatus("Checking scheduled jobs");
+ setProgress(15);
+ // Check if the time for the next import/upload is non-null and in the past, if so, perform it
+ Date nextAction = nextActions.get(configName);
+ if (nextAction == null) {
+ continue;
+ }
+ if (now.after(nextAction)) {
+ setStatus("Performing import and submission for config "+configName);
+ log("Performing import and submission for config "+configName);
+ setProgress(45);
+ Long interval = intervals.get(configName);
+ importData(configName);
+
+
+ // Calculate time for next action and schedule it
+ nextAction = new Date(now.getTime() + interval * 1000);
+ log("Setting next scheduled time for "+configName+" to "+dateFormat.format(nextAction));
+ nextActions.put(configName, nextAction);
+
+ //TODO TEMP for DEV
+ //return null;
+ }
+
+ }
+
+ setStatus("Waiting for scheduled jobs");
+ setProgress(5);
+ // Wait 10 seconds until next check
+ Thread.sleep(10000);
+ }
+ }
+
+ // Trigger import and upload from the GUI
+ private void importData(String configName) {
+ // TODO
+ EcdcAutomationWorker worker = gui.getEcdcAutomationWorker(configName);
+ if (worker!=null) {
+ System.out.println("Starting scheduled activity");
+ worker.execute();
+ log("Job launched, waiting for results");
+ setStatus("Job launched for "+configName+" waiting...");
+ setProgress(60);
+ boolean done = false;
+ while(!done) {
+ try {
+ Thread.sleep(5000);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+
+ if (worker.isDone()) {
+ done = true;
+ log("Finished scheduled activity");
+ }
+
+ }
+
+ } else {
+ log("Null AutomationWorker, job aborted. Please check import config for errors.");
+ }
+
+ }
+}
diff --git a/src/eu/europa/ecdc/enauploader/ImportSqlAuth.java b/src/eu/europa/ecdc/enauploader/ImportSqlAuth.java
new file mode 100644
index 0000000..e7ea953
--- /dev/null
+++ b/src/eu/europa/ecdc/enauploader/ImportSqlAuth.java
@@ -0,0 +1,40 @@
+package eu.europa.ecdc.enauploader;
+
+// Class for holding authentication information for importing from SQL
+public class ImportSqlAuth {
+
+ private int authMethod;
+ private String username;
+ private char[] password;
+
+ public ImportSqlAuth(int authMethod, String username, char[] password) {
+ this.setAuthMethod(authMethod);
+ this.setUsername(username);
+ this.setPassword(password);
+ }
+
+ public int getAuthMethod() {
+ return authMethod;
+ }
+
+ public void setAuthMethod(int authMethod) {
+ this.authMethod = authMethod;
+ }
+
+ public String getUsername() {
+ return username;
+ }
+
+ public void setUsername(String username) {
+ this.username = username;
+ }
+
+ public char[] getPassword() {
+ return password;
+ }
+
+ public void setPassword(char[] password) {
+ this.password = password;
+ }
+
+}
diff --git a/src/eu/europa/ecdc/enauploader/ImportTools.java b/src/eu/europa/ecdc/enauploader/ImportTools.java
new file mode 100644
index 0000000..17aabc7
--- /dev/null
+++ b/src/eu/europa/ecdc/enauploader/ImportTools.java
@@ -0,0 +1,486 @@
+package eu.europa.ecdc.enauploader;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileReader;
+import java.io.IOException;
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.DriverManager;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+
+import javax.swing.JTable;
+import javax.swing.table.DefaultTableModel;
+
+import org.apache.poi.hssf.usermodel.HSSFDateUtil;
+import org.apache.poi.ss.usermodel.CellType;
+import org.apache.poi.ss.usermodel.DataFormatter;
+import org.apache.poi.ss.usermodel.FormulaEvaluator;
+import org.apache.poi.xssf.usermodel.XSSFCell;
+import org.apache.poi.xssf.usermodel.XSSFRow;
+import org.apache.poi.xssf.usermodel.XSSFSheet;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+
+// Class for importing data from various sources
+public class ImportTools {
+
+ private static final String SQL_DRIVER_PREFIX = "jdbc:";
+ private static final String SQLITE_PREFIX = "sqlite://";
+
+
+
+
+ // Map the values using the configured valueMap
+ static ArrayList mapValues(ArrayList data,
+ ArrayList fields, ArrayList indices, LinkedHashMap> valueMap) {
+
+ // Iterate through the data rows
+ for (int j = 0;j valueMapLocal = valueMap.get(field);
+
+ // If there is no value map, do nothing
+ if (valueMapLocal==null) {
+ continue;
+ }
+ // if the old value is in the valueMap, map it and note the entry as updated
+ if (valueMapLocal.containsKey(row[i])) {
+ row[i] = valueMapLocal.get(row[i]);
+ updated = true;
+ }
+
+ }
+
+ // If this entry is updated, write into memory
+ if (updated) {
+ data.set(j, row);
+ }
+ }
+
+ return data;
+ }
+
+
+ // Import from CSV
+ static ArrayList importCsv(ImportConfig cfg, ArrayList oldEntries, ArrayList fields, EcdcJob job, File importFile) {
+ File file = importFile;
+ ArrayList data = new ArrayList();
+
+ try {
+ BufferedReader br = new BufferedReader(new FileReader(file));
+
+ String line;
+
+ while ((line = br.readLine())!=null) {
+ String[] rowData = parseLine(line);
+ data.add(rowData);
+ }
+ br.close();
+
+ } catch (IOException e) {
+ if (job!=null) {
+ job.log("IOException, import failed");
+ job.log(e.getMessage());
+ }
+ e.printStackTrace();
+ }
+ return data;
+ }
+
+
+ // Import from Excel
+ static ArrayList importExcel(ImportConfig cfg, ArrayList oldEntries, ArrayList fields, EcdcJob job, File importFile) {
+
+ File file = importFile;
+ String sheetName = cfg.getDatatable();
+ ArrayList data = new ArrayList();
+ System.out.println(fields.size());
+ ArrayList indices = new ArrayList();
+ for (int i = 0;i cols){
+ cols = tmp;
+ startRow = i;
+ }
+ }
+ }
+ System.out.println(startRow);
+ row = sheet.getRow(startRow);
+ if(row != null) {
+ for(int c = 0; c < cols; c++) {
+ cell = row.getCell((short)c);
+ if(cell != null) {
+ String value = cell.getStringCellValue();
+ int index = fields.indexOf(value);
+ if (index!=-1) {
+ indices.set(index, c);
+ }
+ }
+ }
+ }
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
+
+ for(int i = startRow + 1; i < rows; i++) {
+ row = sheet.getRow(i);
+ if(row != null) {
+ cols = sheet.getRow(i).getPhysicalNumberOfCells();
+ String[] datarow = new String[indices.size()];
+ int n = 0;
+ for(int c : indices) {
+ cell = row.getCell((short)c);
+ if(cell != null) {
+ String value;
+
+ if (cell.getCellType() == CellType.NUMERIC && HSSFDateUtil.isCellDateFormatted(cell)) {
+
+ value = sdf.format(cell.getDateCellValue());
+ } else {
+ objFormulaEvaluator.evaluate(cell); // This will evaluate the cell, And any type of cell will return string value
+ value = objDefaultFormat.formatCellValue(cell,objFormulaEvaluator);
+ }
+
+ datarow[n] = value;
+ System.out.print(value+"\t");
+
+ }
+ n++;
+ System.out.println(n);
+ }
+ data.add(datarow);
+ }
+ }
+
+ wb.close();
+ return data;
+ } catch (IOException e) {
+ if (job!=null) {
+ job.log("IOException, import failed");
+ job.log(e.getMessage());
+ }
+ e.printStackTrace();
+ }
+
+ return null;
+
+ }
+
+
+
+ static ArrayList importSql(ImportConfig cfg, ArrayList oldEntries, ArrayList fields, EcdcJob job) {
+ try {
+ Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
+ } catch (ClassNotFoundException e) {
+ if (job!=null) {
+ job.log("ClassNotFoundException, import failed");
+ job.log(e.getMessage());
+ }
+ e.printStackTrace();
+ }
+
+ ArrayList data = new ArrayList();
+
+ try {
+ String database = cfg.getDatabase();
+ String databaseServer = cfg.getDatabaseServer().toLowerCase();
+ if (databaseServer.startsWith("sqlserver")) {
+ database = SQL_DRIVER_PREFIX+databaseServer+";DatabaseName="+database;
+ } else if (databaseServer.startsWith("mysql")) {
+ database = SQL_DRIVER_PREFIX+databaseServer+"?DatabaseName="+database;
+ }
+
+
+ System.out.println(database);
+ Connection connection;
+
+ if (cfg.getAuthType()==ImportConfig.AUTH_NONE) {
+ connection = DriverManager.getConnection(database+";integratedSecurity=true");
+ } else if (cfg.getAuthType()==ImportConfig.AUTH_PASSWORD) {
+ connection = DriverManager.getConnection(database, cfg.getUsername(), new String(cfg.getPassword()));
+ } else {
+ return null;
+ }
+
+ String query = constructQuery(cfg,fields);
+
+ Statement stmt = connection.createStatement();
+ ResultSet resultSet = stmt.executeQuery(query);
+ while (resultSet.next()) {
+
+ String[] row = new String[fields.size()];
+ for (int i=1;i<= fields.size();i++) {
+ row[i-1] = resultSet.getString(i);
+ }
+ data.add(row);
+
+ }
+
+ } catch (SQLException e) {
+ if (job!=null) {
+ job.log("SQLException, import failed");
+ job.log(e.getMessage());
+ }
+ e.printStackTrace();
+ return null;
+ }
+
+ return data;
+ }
+
+ static ArrayList importSqlite(ImportConfig cfg, ArrayList oldEntries, ArrayList fields, EcdcJob job) {
+ ArrayList data = new ArrayList();
+ Connection connection;
+ try {
+
+ connection = DriverManager.getConnection(SQL_DRIVER_PREFIX+SQLITE_PREFIX+cfg.getDatafile().replace("\\","/"));
+
+
+ String query = constructQuery(cfg,fields);
+
+ Statement stmt = connection.createStatement();
+ ResultSet resultSet = stmt.executeQuery(query);
+ while (resultSet.next()) {
+
+ String[] row = new String[fields.size()];
+ for (int i=1;i<= fields.size();i++) {
+ row[i-1] = resultSet.getString(i);
+ }
+ data.add(row);
+
+ }
+
+ } catch (SQLException e) {
+ if (job!=null) {
+ job.log("SQLException, import failed");
+ job.log(e.getMessage());
+ }
+ e.printStackTrace();
+ return null;
+ }
+
+ return data;
+ }
+
+
+ private static String constructQuery(ImportConfig cfg, ArrayList fields) {
+ String query = "SELECT ";
+ boolean first = true;
+ for (String f : fields) {
+ if (!first) {
+ query = query + ", ";
+ } else {
+ first = false;
+ }
+ query = query +"["+ f+"]";
+ }
+ query = query + " FROM "+cfg.getDatatable();
+ if (!cfg.getSqlQuery().equals("")) {
+ String filter = cfg.getSqlQuery();
+ if (filter.contains(";")) {
+ filter = filter.substring(0, filter.indexOf(";"));
+ }
+
+ query = query + " WHERE "+filter;
+ }
+ query = query + ";";
+ return query;
+ }
+
+
+
+ public static ArrayList readCsvHeader(File file) {
+ ArrayList headers = new ArrayList();
+ try {
+ BufferedReader br = new BufferedReader(new FileReader(file));
+ String line = br.readLine();
+ String[] fields = parseLine(line);
+ for (String f : fields) {
+ headers.add(f);
+ }
+ br.close();
+ return headers;
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ return null;
+ }
+
+ public static ArrayList readExcelHeader(File file, String sheetName) {
+ ArrayList headers = new ArrayList();
+ FileInputStream fs;
+ try {
+ fs = new FileInputStream(file);
+ XSSFWorkbook wb = new XSSFWorkbook(fs);
+ XSSFSheet sheet = wb.getSheet(sheetName);
+ XSSFRow row;
+ XSSFCell cell;
+
+ int rows; // No of rows
+ rows = sheet.getPhysicalNumberOfRows();
+
+ int cols = 0; // No of columns
+ int tmp = 0;
+ int startRow = 0;
+
+ // This trick ensures that we get the data properly even if it doesn't start from first few rows
+ for(int i = 0; i < 5 && i < rows; i++) {
+ row = sheet.getRow(i);
+ if(row != null) {
+ tmp = sheet.getRow(i).getPhysicalNumberOfCells();
+ if(tmp > cols){
+ cols = tmp;
+ startRow = i;
+ }
+ }
+ }
+ System.out.println(startRow);
+ row = sheet.getRow(startRow);
+ if(row != null) {
+ for(int c = 0; c < cols; c++) {
+ cell = row.getCell((short)c);
+ if(cell != null) {
+ String value = cell.getStringCellValue();
+ headers.add(value);
+ }
+ }
+ }
+ wb.close();
+ return headers;
+ } catch (IOException e) {
+
+ e.printStackTrace();
+ }
+
+ return null;
+ }
+
+ public static ArrayList readSqliteHeader(String path, String table) {
+ ArrayList headers = new ArrayList();
+
+ Connection connection;
+ try {
+ System.out.println(SQL_DRIVER_PREFIX+SQLITE_PREFIX+path.replace("\\","/"));
+ connection = DriverManager.getConnection(SQL_DRIVER_PREFIX+SQLITE_PREFIX+path.replace("\\","/"));
+ DatabaseMetaData md = connection.getMetaData();
+ ResultSet rset = md.getColumns(null, null, table, null);
+ while (rset.next())
+ {
+ headers.add(rset.getString("COLUMN_NAME"));
+ }
+ return headers;
+ } catch (SQLException e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ public static ArrayList readSqlHeader(String databaseServer,String database, String table, ImportSqlAuth auth) {
+
+ try {
+ Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
+ } catch (ClassNotFoundException e) {
+ e.printStackTrace();
+ }
+
+ String dbName = database;
+ databaseServer = databaseServer.toLowerCase();
+ if (databaseServer.startsWith("sqlserver")) {
+ database = SQL_DRIVER_PREFIX+databaseServer+";DatabaseName="+database;
+ } else if (databaseServer.startsWith("mysql")) {
+ database = SQL_DRIVER_PREFIX+databaseServer+"?DatabaseName="+database;
+ }
+
+
+ System.out.println(database);
+ Connection connection;
+ try {
+ if (auth.getAuthMethod()==ImportConfig.AUTH_NONE) {
+ connection = DriverManager.getConnection(database+";integratedSecurity=true");
+ } else if (auth.getAuthMethod()==ImportConfig.AUTH_PASSWORD) {
+ connection = DriverManager.getConnection(database, auth.getUsername(), new String(auth.getPassword()));
+ } else {
+ return null;
+ }
+
+ return readGeneralSqlHeaders(connection,dbName,table);
+ } catch (SQLException e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+
+ private static ArrayList readGeneralSqlHeaders(Connection connection, String db, String table) throws SQLException {
+ ArrayList headers = new ArrayList